Ghost + NGINX + Docker, OH MY

Tue 10 July 2018
By roy.

Ghost + NGINX + Docker, OH MY

So, I recently decided to redo my website and this is a active work in progress.




I've been using this as an excuse to finally get my shit together and actually learn Docker.

Up until now I've done basic docker work (docker run hello-world, etc...), but I haven't done anything particularly serious with it.

I'm not going to get into details and explain the internals and how docker works. I'm not super great at that, plus, there ware way smarter people who've beaten that horse to death.

Putting it together

So, after some furious research (aka, typing the words "docker", "ghost", and "blog" into google) I found their official docker image.

I then went and set up a new VM in DigitalOcean to act as my docker host with a external volume to hold all the persistent data (blog DB, config info for nginx, etc...) so that all can be easily backed up and I don't have to worry about it.

At first, my set-up was kinda clunky. I was building containers and running those with all the static content (, keybase challenge) pre-baked into the container. For simple set-ups that don't change - this is fine for the most part.

Here was the initial Dockerfile I was using to run nginx:

FROM nginxMAINTAINER Roy LarsenCOPY nginx.conf /etc/nginx/nginx.confCOPY keybase.txt /usr/share/nginx/htmlCOPY tde/* /usr/share/nginx/html/tde/

See? Not very exciting, not very portable/maintainable since now I need to maintain my own container that only exists on this one server.

Part of the portability, maintainability problem was was made this blog possible: a container running ghost.

So, now I have two containers that are essentially running the default software and I'm re-building at least one of them.

Enter Compose:

So, the first big change that I made was create a symlink on the host to be what the containers expect for their persistent storage instead of building the containers with the static content inside of them.

Here's how I'm now defining my containers:

version: "2.2"services:        nginx:               image: "nginx:latest"               ports:                        - "80:80"                        - "443:443"               volumes:                       - /docker/nginx/conf/nginx.conf:/etc/nginx/nginx.conf                       - /docker/nginx/www:/usr/share/nginx/html               networks:                        - default               links:                       - "blog"        blog:                image: "ghost:1-alpine"                volumes:                        - /docker/ghost/:/var/lib/ghost/content                environment:                        - URL=                networks:                        - defaultnetworks:        default:                external:                        name: web

When that file is in place, all you need to do is sudo docker-compose up -d from that directory and you're good to go.

So, the nice thing about this is that when I want to tackle that first bullet point in Future, I can just have the container hosting certbot place the certs into the volume mapped to /docker/nginx/conf/certs and that will be that.

For portability, as long as the appropriate drive is mapped and symlink is created - I can always spin up these containers the same way every time. It's pretty sweet.


  • ~~Let's Encrypt for doing SSL.~~ Ha, notice how this is served over HTTPS? Need to write that article
  • ~~ Ha, Need to write that up
  • Separating internal and external container networks.
  • Ansible to prep the host server
  • ~~Get more serious about Docker on Windows (I know: boo, hiss, etc...)~~ Ha, I wrote this