codes, bugs & demons

TL;DR GitHub Repository, DockerHub Image 👻

UPDATE The docker-compose file was updated to change database from MySQL to MariaDB, the reason is explained here.

After struggling for a while trying to host this website using Ghost on Docker i finally built a good recipe that i'm going to share today, maybe someone will suggest some kind of improvement.

The goal was to set up Ghost on a Docker host machine with a Cloud asset storage (AWS or DigitalOcean) and enable HTTPS using Let’s Encrypt for certificate generation and auto-renewal.

This recipe can either be used via docker-compose or docker stack deploy (on Swarm enabled Docker hosts), i created a GitHub Repository with docker-compose files and some useful scripts.

Let's take a look at the custom Dockerfile, it's pretty clear, from a Ghost v2 image i navigate to the /var/lib/ghost folder to install AWS and DigitalOcean cloud storage packages:

# using ghost version 2
FROM ghost:2

# set ghost folder as workdir
WORKDIR /var/lib/ghost

# create storage adapters folder
RUN mkdir -p ./content.orig/adapters/storage

# install ghost-digitalocean npm package
# https://github.com/shiva-hack/ghost-digitalocean
RUN npm install ghost-digitalocean && \
    cp -r ./node_modules/ghost-digitalocean \
    ./content.orig/adapters/storage/digitalocean

# install ghost-storage-adapter-s3 npm package
# https://github.com/colinmeinke/ghost-storage-adapter-s3
RUN npm install ghost-s3-storage-adapter && \
    cp -r ./node_modules/ghost-s3-storage-adapter \
    ./content.orig/adapters/storage/s3

And here is the docker-compose file:

version: '3.6'

networks:
  default:
    driver: overlay
    attachable: false

volumes:
  ssl-certs:
  ghost-db:
  ghost-content:
    # driver: local-persist
    # driver_opts:
    #   mountpoint: ${PWD}/ghost

services:
  nginx:
    container_name: nginx
    image: valian/docker-nginx-auto-ssl
    environment:
      ALLOWED_DOMAINS: yourdomain.com
      SITES: "yourdomain.com=ghost:2368"
      FORCE_HTTPS: "true"
    volumes:
      - ssl-certs:/etc/resty-auto-ssl
    networks:
      - default
    ports:
      - "80:80"
      - "443:443"
    restart: always

  db:
    container_name: ghost-db
    image: mariadb:10
    restart: always
    volumes:
      - ghost-db:/var/lib/mysql
    networks:
      - default
    environment:
      MYSQL_DATABASE: ghost
      MYSQL_ROOT_PASSWORD: password

  ghost:
    container_name: ghost
    image: macchie/ghost-scs
    restart: always
    volumes:
      - ghost-content:/var/lib/ghost/content
    networks:
      - default
    environment:
      ENV: production
      NODE_ENV: production
      url: https://yourdomain.com
      admin_url: https://yourdomain.com
      database__client: mysql
      database__connection__host: db
      database__connection__user: username
      database__connection__password: root
      database__connection__database: ghost
      storage__active: digitalocean # or 's3' for AWS storage
      GHOST_DO_KEY: DO_KEY
      GHOST_DO_SECRET: DO_SECRET
      GHOST_DO_REGION: DO_REGION
      GHOST_DO_BUCKET: DO_BUCKET
      GHOST_DO_ENDPOINT: DO_ENDPOINT
      GHOST_DO_SUBFOLDER: DO_SUBFOLDER
      GHOST_DO_SPACE_URL: DO_SPACE_URL
      # in case you want to use AWS S3 for storage customize these vars
      # AWS_ACCESS_KEY_ID
      # AWS_SECRET_ACCESS_KEY
      # AWS_DEFAULT_REGION
      # GHOST_STORAGE_ADAPTER_S3_PATH_BUCKET
      # GHOST_STORAGE_ADAPTER_S3_ASSET_HOST  // optional
      # GHOST_STORAGE_ADAPTER_S3_PATH_PREFIX // optional
      # GHOST_STORAGE_ADAPTER_S3_ENDPOINT // optional
      # GHOST_STORAGE_ADAPTER_S3_SSE // optional
      # GHOST_STORAGE_ADAPTER_S3_FORCE_PATH_STYLE // optional

As you can see, it combines three Docker images:

  • valian/docker-nginx-auto-ssl i found this to be a good solution to enable SSL, basically it's a nginx proxy that can be configured via env variables for multi-domain certificate generation and renewal
  • mariadb:10 switched from MySQL to MariaDB for our Ghost database, reason
  • macchie/ghost-scs that's the image you will build using the Dockerfile, an hosted version is also available on DockerHub so you don't actually need to build your own unless you need further customization

In addition you can use a custom driver for your Ghost content volume, i used local-persist, its just a normal docker volume but you can specify it's path on the disk and the data won't get deleted if you destroy the volume.

To install local-persist driver on your system just run this command and uncomment the volumes driver options in your compose file:

curl -fsSL https://raw.githubusercontent.com/CWSpear/local-persist/master/scripts/install.sh | sudo bash

In conclusion, spin up your configuration with:

docker-compose up --build

on you development machine, or deploy a stack on a Docker Swarm by running:

docker stack deploy -c docker-compose.production.yml MYSTACKNAME

Additional informations and resources you may want to check in case of problems:

Enjoy your new Ghost blog :)