Create a blog using Ghost

This guide covers setting up a Ghost blog using Docker on Debian. You'll learn how to configure Docker, set up Ghost with MariaDB, and route it to your domain using Nginx. I also cover SSL setup with Certbot and options for hosting via Cloudflare Tunnels or a VPS.

Share

(How I created this blog, A step by step guide)

About this guide

  • This is focused on setting up a Blog using Ghost via Docker on Debian (or its derivatives). While you can use this on a VPS, I used my own server that I made out of a mini-PC. More on that in a future post.
  • Every underlined word/set of words is a relevant link.
  • I created this guide because I couldn't find simple & concise instructions to set up Ghost fast on a Linux system using docker.

Initial Setup

  1. Open a terminal window (or a shell).
  2. Install docker if not already installed.
  3. Make sure you have the appropriate permissions to use Docker in your system. (you may need to add your user to the Docker group).

The main course

variety of food on mat
Photo by Sara Dubler / Unsplash

Follow these steps to set up Ghost in the fastest and cleanest way possible.

I prefer installing new software using Docker because if I mess up, or simply don't like the software, I can remove all traces of it using standard docker container, image and volume commands.

Whereas, installing something on your PC directly makes changes/adds more dependent software that you either have to manually keep track of, or forget to remove. This with time, clutters up your PC and strongly affects performance.

Steps:

  1. At your desired place, create a new directory, let's call it Ghost.
    1. mkdir Ghost
  2. Navigate to this directory, and create a new file docker-compose.yml. This is the YAML file that we will use to tell docker what to do. Open this file and paste the code below.
    1. cd Ghost
    2. touch docker-compose.yml
    3. Open using any editor, I'm using nano because of simplicity. Type nano docker-compose.yml .
    4. Copy this configuration and paste it into the file:
services:
  ghost:
    image: ghost:latest
    restart: always
    ports:
      - "2368:2368"
    depends_on:
      - db
    environment:
      url: <YOUR_BLOG_URL>
      database__client: mysql
      database__connection__host: db
      database__connection__user: ghost
      database__connection__password: ghostdbpass
      database__connection__database: ghostdb
    volumes:
      - /home/ghost/content:/var/lib/ghost/content
      - type: bind
        source: /location/of/config.production.json
        target: /var/lib/ghost/config.production.json

  db:
    image: mariadb:latest
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: <YOUR_ROOT_PASSWORD>
      MYSQL_USER: ghost
      MYSQL_PASSWORD: ghostdbpass
      MYSQL_DATABASE: ghostdb
    volumes:
      - /home/ghost/mysql:/var/lib/mysql

docker-compose.yml

  1. Edit the docker-compose.yml file to fit your blog. Going from the top-down, following changes need to be made:
    1. (Under environment) url : Instead of <YOUR_BLOG_URL> enter the URL of the website where the blog will be accessible. Even if you're using a reverse proxy, the URL must be the public facing one, instead of the local URL. For example, for this blog it was https://blog.samirkabra.com .
    2. (Under volumes) source: Replace /location/of/config.production.json with the current dir/config.production.json .
      1. Get current directory using pwd. Example: /home/user/Ghost/ , so then we would replace it with /home/user/Ghost/config.production.json.
      2. This file doesn't exist yet, do not worry, we will create it.
    3. (In db > environment) MYSQL_ROOT_PASSWORD: Replace <YOUR_ROOT_PASSWORD> with a strong password consisting of letters and numbers (You can use symbols, but some symbols like $ can result in erroneous behavior, so I prefer to avoid them entirely).
  2. Save the docker-compose.yml file and exit nano.
    1. ctrl + s
    2. ctrl + x
  3. Now create the configuration file for Ghost. We created a bind mount so that the config we create here is mirrored to the Ghost Docker container.
    1. touch config.production.json
    2. nano config.production.json Paste the following into the file:
{
  "url": "http://localhost:2368",
  "server": {
    "port": 2368,
    "host": "::"
  },
  "mail": {
    "from": "<FROM_EMAIL>",
    "transport": "SMTP",
    "options": {
      "host": "smtp.mailgun.org",
      "port": 587,
      "service": "Mailgun",
      "secure": false,
      "requireTLS": true,
      "auth": {
        "user": "<YOUR_USER>",
        "pass": "<YOUR_PASS>"
      }
    }
  },
  "logging": {
    "transports": [
      "file",
      "stdout"
    ]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/lib/ghost/content"
  }
}
  1. Configure Mailgun using this Official Tutorial by Ghost. Fill out the credentials within the config file after setting up SMTP credentials on Mailgun.
  2. Save this and exit nano.
  3. Use the following command to start the docker container.
docker compose up -d
  1. Check out your local instance of Ghost at localhost:2368 or VPS_IP_ADDRESS:2368. To access the admin panel, visit localhost:2368/ghost or VPS_IP_ADDRESS:2368/ghost.

Routing Ghost to Your Domain

Depending on where you've hosted Ghost, there are a few ways:

If you're self hosting
  • Look into Cloudflare Tunnels. Without exposing a port, you can host the Ghost instance on your domain. However, this requires Cloudflare to be your DNS provider. If you do not want to do that, we have a few alternatives.
  • Expose a port on your router, request your ISP to provision you a static IP address, and then follow the steps for VPS. (I don't recommend this, and I have never done this because of security concerns.)
  • Use a VPN like Tailscale or WireGuard that you can either:
    • Give access to your users to (Tailscale has a limited amount of users you can share it with on their free plan.)
    • Get a lightweight VPS that acts as a public facing intermediary that is connected to your VPN.
If you're using a VPS
  1. Pointing a Domain to a VPS:
    1. Ensure your VPS provider assigns a static IP to your server.
    2. Set up an A record in your domain registrar's DNS settings, pointing to the VPS IP.
    3. You can then configure a reverse proxy using Nginx or Traefik to forward traffic to Ghost.
  2. Using a Reverse Proxy:
    1. Install Nginx or Traefik on the VPS and configure it to route requests for your domain to the Ghost container:
server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://localhost:2368;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
  1. SSL Configuration
    1. Use Certbot to generate SSL certificates for your domain. Certbot can automatically configure Nginx for SSL:
sudo certbot --nginx -d yourdomain.com

Final Thoughts

By following this guide, you should now have a fully functioning Ghost blog up and running using Docker on your Debian-based system. Docker makes it easy to manage, maintain, and scale your application while keeping your system clean and clutter-free. Additionally, I’ve provided options for routing Ghost to your domain, whether you’re self-hosting or using a VPS.

If you run into any issues, feel free to revisit the official documentation or explore alternative configurations that best suit your setup.

What's Next?

In future posts, I plan to cover:

  • How I built my own mini-server out of a mini-PC.
  • Other OSS that I host on my mini-PC.

If you're interested in these topics, or would like the ability to comment make sure to subscribe. It's free and always will be!

Get in Touch

If you have questions or suggestions, feel free to reach out via Twitter or drop me an email at [email protected]. I’d love to hear your thoughts or help troubleshoot any issues you encounter!


Thank You on wooden blocks
Photo by Courtney Hedger / Unsplash

Samir | www.samirkabra.com