So before I put this site up I couldn't find many good articles or guides on getting Ghost setup with Let's Encrypt. Thankfully I already have a few services that use a similar configuration I've used in the docker-compose file for this blog. I'm just going to go over it a little bit and hopefully somebody else can find this to be helpful.

Ghost conveniently comes with a Docker image. To use this all we need to do is specify it in with the image tag. Aside from that the only real configuration I had to do was set the url env variable to this website's URL. This basically tells Ghost where to refer a user. So if you have it set as localhost for instance, when you clicked Author at the top, or any other hyperlink and you had it set to 'localhost', you'd get redirected to 'localhost/author'.

ghost:
  image: ghost:3-alpine
  restart: always
  ports:
    - 2368:2368
  environment:
    database__client: mysql
    database__connection__host: db
    database__connection__user: root
    database__connection__password: [Redacted]
    database__connection__database: ghost
    url: https://hexed.io
Take note of the url, I have it set to redirect to https. Also the database__connection__password should match the password you're going to set in the MySQL service.

Next we need to setup the MySQL service, pretty standard stuff. Just use the MySQL image. By default the user created will be root unless you specify otherwise. Ghost will also take care of creating the ghost database for you. Just make sure the password you specify matches the one you've defined in the ghost service above.

db:
  image: mysql:5.7
  restart: always
  environment:
    MYSQL_ROOT_PASSWORD: [Redacted]
  volumes:
    - db_data:/var/lib/mysql

After you setup ghost and the db all you really have left to do is serve the app. To do so we're going to use Nginx with a nice tool called docker-letsencrypt-nginx-proxy-companion and another called nginx-proxy.

To do so we create three new services. One for the proxy, another for the LetsEncrypt proxy companion, and of course lastly the Nginx.

proxy:
  container_name: proxy
  environment:
    - DEFAULT_HOST=hexed.io
  image: jwilder/nginx-proxy
  labels:
      com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true"
  logging:
    options:
      max-size: "200k"
      max-file: "10"
  ports:
    - "80:80"
    - "443:443"
  restart: always
  volumes:
    - /var/run/docker.sock:/tmp/docker.sock:ro
    # Increase gateway timeout.
    - ./proxy.conf:/etc/nginx/proxy.conf
    - letsencrypt_certs:/etc/nginx/certs
    - letsencrypt_challenge:/usr/share/nginx/html
    - letsencrypt_vhosts:/etc/nginx/vhost.d
It's absolutely necessary we keep port 80 open for the generation of the SSL certificates. Also take note of the letsencrypt_nginx_proxy_companion label.

As you can see above I'm using a proxy.conf here. This is what it looks like:

# START OF DEFAULT SETTINGS

# HTTP 1.1 support
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
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 $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;

# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";

# END OF DEFAULT SETTINGS

proxy_read_timeout 300s;

Once we have our proxy and have it configured to be compatible with the Let's Encrypt proxy companion we need to setup the letsencrypt service.

letsencrypt:
  container_name: letsencrypt
  environment:
    - NGINX_PROXY_CONTAINER=proxy
  image: jrcs/letsencrypt-nginx-proxy-companion
  restart: always
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock:ro
    - letsencrypt_certs:/etc/nginx/certs
    - letsencrypt_challenge:/usr/share/nginx/html
    - letsencrypt_vhosts:/etc/nginx/vhost.d

Configuration is simple enough. Just need to specify the proxy container and the volumes to persist the certificates to. I'd advise you to check out the letsencrypt proxy companion repo more if you're interested in knowing its implementation details.

Once both of these are setup the only thing we have left to do is setup the Nginx service and the volumes for MySQL persistence and the LetsEncrypt certs etc.

First let's start with the Nginx service.

nginx:
  container_name: nginx
  depends_on:
    - ghost
  environment:
    - VIRTUAL_HOST=www.hexed.io,hexed.io
    - LETSENCRYPT_HOST=www.hexed.io,hexed.io
    - LETSENCRYPT_EMAIL=[Redacted]
  image: nginx:1.15.6-alpine
  logging:
    options:
      max-size: "200k"
      max-file: "10"
  restart: always
  volumes:
    - ./default.conf:/etc/nginx/conf.d/default.conf

This is a pretty simple configuration, take note of the environment variables. You can replace these with your own. I believe most settings are pretty clear on what you'd change to your own. Now for the Nginx configuration I'm using.

server {
    listen 80;
    server_name hexed.io www.hexed.io;

    location / {
        proxy_pass http://ghost:2368;

        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}
Take note of the listen 80. The proxy companion says you need to keep your app open on 80 or else certs can't be verified. The proxy container will handle serving your app on 443.

It's a pretty simple Nginx conf. I don't currently have it setup to handle caching but I'll go over that in a subsequent post when I get around to setting the rest up. For now I didn't really find it necessary, just wanted something to write posts on for now.

The last thing to do is just setup the volumes.

volumes:
  db_data:
  letsencrypt_certs:
  letsencrypt_challenge:
  letsencrypt_vhosts:
  

And that's it, now you can deploy your blog on your VPS or cloud granted you have docker setup on your machine. All you should need to do is docker-compose up -d --build.

Here's a link to the source:

netsudo/ghost-blog
Just configs for my personal blog. Contribute to netsudo/ghost-blog development by creating an account on GitHub.