Self host ghost blog with docker

Previously, in this post, I have talked how to start a ghost blog locally with docker. Now I will show the complete compose file I use for this ghost blog with https and domain set.

For a self-hosted blog, you need 3 things:

  1. A server with public ip. It’ s the host of the blog. You can buy a VPS which is cheap and convenient. My blog is host on Vultr.
  2. A domain name for your blog which can route to the server. I bought the domain from namecheap.
  3. A https enabled blog system. Definitely, I will use ghost.

Why docker?

For easy installation, update and migration. If you ever try to update ghost or move the blog from one machine to another, you will know what docker can buy us.

The compose file is at the bottom. You should change the domain name, email and password accordingly. It has 4 services.

  1. ghost, the ghost blog with the latest official image. The port is 2368.
  2. mysql, ghost database.
  3. nginx-proxy. Auto nginx reverse proxy server for docker.
  4. letsencrypt. Auto create, renew SSL certificate for https.
version: "3"

services:
  ghost:
    image: ghost
    container_name: ghost
    volumes:
      - ghostdata:/var/lib/ghost/content:rw
    environment:
      - url=**https://yourdomain.me**
      - database__client=mysql
      - database__connection__host=mysql
      - database__connection__user=ghost
      - database__connection__password=**ghostpassword**
      - database__connection__database=ghostdb
      - VIRTUAL_HOST=**yourdomain.me**
      - VIRTUAL_PORT=2368
      - LETSENCRYPT_HOST=**yourdomain.me**
      - LETSENCRYPT_EMAIL=**your_email_address**
    expose:
      - "2368"
    networks:
      - net
    depends_on:
      - mysql
    restart: unless-stopped

  mysql:
    image: mysql
    container_name: mysql
    volumes:
      - ghostdb:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=**rootpassword**
      - MYSQL_DATABASE=ghostdb
      - MYSQL_USER=ghost
      - MYSQL_PASSWORD=**ghostpassword**
    expose:
      - "3306"
    networks:
      - net
    restart: unless-stopped

  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    volumes:
      - certs:/etc/nginx/certs:ro
      - vhost:/etc/nginx/vhost.d
      - html:/usr/share/nginx/html
      - /var/run/docker.sock:/tmp/docker.sock:ro
    ports:
      - "80:80"
      - "443:443"
    networks:
      - net
    labels:
      - com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true
    restart: unless-stopped

  letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion:v1.13.1
    volumes:
      - certs:/etc/nginx/certs
      - vhost:/etc/nginx/vhost.d
      - html:/usr/share/nginx/html
      - acme:/etc/acme.sh
      - /var/run/docker.sock:/var/run/docker.sock:ro
    volumes_from:
      - nginx-proxy
    networks:
      - net
    restart: unless-stopped

volumes:
  certs:
  vhost:
  html:
  acme:
  ghostdb:
  ghostdata:

networks:
  net:
    driver: bridge