Since I created this blog on Ghost, I tasked my self with the goal of running everything on this droplet using Docker. I believe the benefits of using Docker far outweight all the roadblocks I have stumbled upon while learning how to use it. If you don’t know what Docker is, you better go check it out now
If you are interested in getting a copy of my current docker setup, check out and fork this github repo
Initially, I had all my setup in a single
docker-compose.yml file and everything was working just fine. But eventually, I plan to have more than one website in my current droplet and I didn’t want everything to be together in a large monolith. Thus, I decided to break services down in different compose files.
The only side effect I am experiencing right now is in the way I call docker-compose commands. Since I no longer have a
docker-compose.yml file, I have to resort to issue the commands using:
$ docker-compose -f filename.yml COMMAND
But that is small price to pay and I am more than happy with my current workflow.
For the nginx setup, I had created in advance an external bridge network named ngixn_frontend in the docker host as follow:
$ docker network create nginx_frontend
My nginx compose file is comprised of 3 services:
- Nginx: Uses the official nginx image from docker hub.
- Nginx-gen: From jwilder, this awesome image will automatically generate the reverse proxy configs used by the Nginx service to connect towards the running apps. In this particular case to the Ghost blog app.
- nginx-letsencrypt: From JrCs, it enhances the nginx proxy setup, by handling creation/renewal of Let’s Encrypt certificates.
version: "3" services: nginx: image: nginx container_name: nginx ports: - '80:80' - '443:443' volumes: - nginx-conf:/etc/nginx/conf.d - nginx-vhost:/etc/nginx/vhost.d - nginx-html:/usr/share/nginx/html - nginx-certs:/etc/nginx/certs:ro labels: com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: 'true' restart: always networks: - nginx_frontend nginx-gen: image: jwilder/docker-gen command: -notify-sighup nginx -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf container_name: nginx-gen restart: always volumes: - nginx-conf:/etc/nginx/conf.d - nginx-vhost:/etc/nginx/vhost.d - nginx-html:/usr/share/nginx/html - nginx-certs:/etc/nginx/certs:ro - /var/run/docker.sock:/tmp/docker.sock:ro - ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro networks: - nginx_frontend nginx-letsencrypt: restart: always image: jrcs/letsencrypt-nginx-proxy-companion container_name: nginx-letsencrypt volumes: - nginx-conf:/etc/nginx/conf.d - nginx-vhost:/etc/nginx/vhost.d - nginx-html:/usr/share/nginx/html - nginx-certs:/etc/nginx/certs:rw - /var/run/docker.sock:/var/run/docker.sock:ro environment: - NGINX_DOCKER_GEN_CONTAINER=nginx-gen - NGINX_PROXY_CONTAINER=nginx networks: - nginx_frontend networks: nginx_frontend: external: true volumes: nginx-vhost: nginx-conf: nginx-html: nginx-certs:
Most of the configuration is based from the documentation of the respective images. One tiny detail to watch out is the necessity of declaring nginx_frontend network as external to avoid generating a new network when running docker-compose.
My Ghost app is configured as shown below in a
blog.compose.yml file. The compose file declares to network: the nginx_frontend as external one
It reads several environment variables from a .env file that includes the host directory binds for the config.production.json and the theme directory. The configuration also specifies the values for environment vars used by the nginx-gen and nginx-letsencrypt images to automatically plug in the ghost app with nginx.
The mysql db configuration below is pretty straightforward and the only remark is that I am issuing a command to specify that MySQL should use utf8mb4 for the character set and collation.
Both services communicate between them by the blog_backend network. Besides, the ghost container connects with ngixn services by virtue of being part of the nginx_frontend network.
version: "3" services: ghost: image: ghost container_name: blog-app expose: - "$GHOST_PORT" restart: always volumes: - ghost_content:/var/lib/ghost/content - $GHOST_THEME:/var/lib/ghost/content/themes/BitKnown - $GHOST_CONFIG:/var/lib/ghost/config.production.json environment: - VIRTUAL_HOST=$BLOG_HOST,www.$BLOG_HOST - LETSENCRYPT_HOST=$BLOG_HOST - LETSENCRYPT_EMAIL=$EMAIL - NODE_ENV=production depends_on: - db networks: - nginx_frontend - blog_backend db: image: mysql container_name: blog-db command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci volumes: - blog_db:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORD=$GHOST_DB_PASSWORD - MYSQL_DATABASE=$GHOST_DB_NAME networks: - blog_backend networks: nginx_frontend: external: true blog_backend: volumes: ghost_content: blog_db:
This post was a shorter one, but it took me some time to get everything working properly under the hood. This configuration provides me a lot of flexibility for future endeavors. I can say that I am very happy with the results so far and it has give me a reason to dig deeper in Docker.
Well, this is it for today. I hope this bit of Docker information has been of some help. Once again, thanks for reading till the end. See you soon and stay tuned for more!!