Self-Host Ghost Blog with Mysql and Traefik

DevOps Jul 2, 2022

Ghost is a very popular open-source CMS. It is a great alternative to WordPress and Substack because its easy-to-use and also has several membership options. One can easily start a blog or a membership-based newsletter with a few clicks from the Ghost admin dashboard.

There are many managed Ghost hosting providers out there which manage the hosting, backup, SMTP and other backend stuff for you. But it may not be very budget-friendly for everyone.

In this small post, we are going to self-host our Ghost blog with Mysql in Docker and reverse proxy it using Traefik.

Prerequisites

  • A domain name for your blog. Try to go with .com and make sure it's professional and suits your needs.
  • (Optional) An SMTP service for setting up email and newsletter. You can try Mailgun as recommended by Ghost (Not free).
  • A server with Docker and Traefik installed.
If you haven't gone through the installation of Traefik, I would recommend that you follow this guide before reading further.
Simplify your deployment with Traefik - A comprehensive walk-through
Deploying docker containers, managing routes, generating TLS certificates, and load balancing can be a hassle sometimes. It may become more difficult to manage all the stuff if you want to run multiple docker containers on the same host!

Step 1: Creating Config files for Ghost

Create a directory for storing our docker-compose.yml file and create a sub-directory blog for storing Ghost configuration file as well as a directory content under blog for storing images and logging info.

mkdir -p ghost-blog/blog/content && \
  cd ghost-blog && \
  touch ./blog/config.production.json && \
  nano ./docker-compose.yml

This should be the final tree:

├── ghost-blog
│   ├── docker-compose.yml
│   └── blog
│       ├── config.production.json
│       └── content
 
version: '3.7'

services:
  ghost:
    image: ghost:5.2.3
    restart: always
    depends_on:
      - db
    environment:
      NODE_ENV: production
    networks:
      - default
      - web
    volumes:
      - ./blog/content:/var/lib/ghost/content
      - ./blog/config.production.json:/var/lib/ghost/config.production.json
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=web"
      - "traefik.http.routers.ghost-secure.entrypoints=websecure"
      - "traefik.http.routers.ghost-secure.rule=Host(`fossian.com`)"
      - "traefik.http.routers.ghost-secure.service=ghost-service"
      - "traefik.http.services.ghost-service.loadbalancer.server.port=2368"

  db:
    image: mysql:oracle
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: your_mysql_root_password
      MYSQL_USER: ghost
      MYSQL_PASSWORD: ghostdbpass
      MYSQL_DATABASE: ghostdb
    networks:
      - default
    volumes:
      - ./data:/var/lib/mysql
      
networks:
  web:
    external: true
💡
Official Docker images of Mysql now support multi-architecture (amd64 & arm64) with the tag oracle.

Don't forget to change fossian.com to your domain on line 21. Also, change the database user and passwords under db service (line 29 - 32).

Now edit the main Ghost configuration file config.production.json.

nano ./blog/config.production.json
 {
  "url": "https://fossian.com",
  "server": {
    "port": 2368,
    "host": "0.0.0.0"
  },
  "database": {
    "client": "mysql",
    "connection": {
      "host": "db",
      "port": 3306,
      "user": "ghost",
      "password": "ghostdbpass",
      "database": "ghostdb"
    }
  },
  "mail": {
    "transport": "SMTP",
    "options": {
      "service": "Mailgun",
      "auth": {
      	"user": "[email protected]",
      	"pass": "1234567890"
      }
    }
  },
  "logging": {
    "path": "/var/lib/ghost/content/logs/",
    "level": "info",
    "rotation": {
      "enabled": true,
      "count": 15,
      "period": "1d"
    },
    "transports": ["stdout", "file"]
  },
  "paths": {
    "contentPath": "/var/lib/ghost/content"
  }
}
  • Change the URL on line 2 to your Ghost blog URL.
  • Change the database credentials according to what you filled in docker-compose.yml (line 12 - 14).
  • If you want to enable Membership and Newsletter functionality then make sure you have an SMTP server. I recommend using mailgun as it supports the bulk transfer feature. Change the SMTP credentials on lines 18 to 25 to yours.
    If you don't want to enable it then simply change it as shown below.
"mail": {
    "transport": "Direct",
},

You can check out Ghost's official Configuration page for more info.

Step 2: Deploying

After setting up everything, simply use docker-compose  to deploy your Ghost blog.

docker-compose up -d

That's it! You have a fully functional Ghost blog running in Docker and proxying with Traefik. Make sure to log in to your admin dashboard at https://yourdomain.tld/ghost.

Feel free to comment below if you have any issues regarding the installation!

Tags

Amresh Sinha

Developer👨‍💻 | Student🎓 | Traveller✈️