Blog Setup: Docker, Ghost, SSL - Easy Guide
Setup Ghost Blog: Deploy with Docker, configure reverse proxy, SSL, MariaDB for seamless experience.

This week I was researching and looking for services that would help me manage my blog, while doing this research I came across Ghost, a service that despite not being the one chosen for this migration, the truth is that I liked it a lot, as I had a bit of a fight to be able to After setting it up, I decided to create this post to share the process and the resulting docker-compose.
As I told you, we are going to install Ghost using Docker, in this case we will use a docker-compose.yml file that will describe how the different services that we will require for said installation should be deployed and configured. This process involves several services such as a reverse proxy (nginx-proxy), an SSL certificate generator (letsencrypt), a database (mariadb), and Ghost itself. Here's how to configure each of these.
Before starting
In this guide I will assume that we already have docker and docker compose installed in our OS. If you do not have it installed, I attach a link to the official documentation to facilitate the process.
Reverse Proxy Configuration (nginx-proxy)
The nginx-proxy service acts as a reverse proxy for Docker containers. Facilitates access to services through hostnames and manages Nginx configuration automatically.
We will use the image: jwilder/nginx-proxy, in addition to exposing ports 80 and 443 for HTTP and HTTPS traffic, we must also mount several volumes to share Docker sockets, SSL certificates and host configurations, finally We will configure a tag to be able to integrate with the letsencrypt-nginx-proxy-companion plugin which will allow us to generate the SSL certificate for our domain, we will see this in more detail in the next step.
version: '3'
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
ports:
- '80:80'
- '443:443'
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- certs:/etc/nginx/certs:rw
- vhost.d:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
labels:
- com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy
SSL Certificate Generator (Letsencrypt)
The letsencrypt service provides automatic SSL certificates for configured domains.
We will use the image: jrcs/letsencrypt-nginx-proxy-companion, it is important to indicate that this container depends on the one configured previously: nginx-proxy, as for the volumes we will use the same ones as nginx-proxy to access the configuration and store the certificates, finally we must define the variable NGINX_PROXY_CONTAINER to indicate the dependency with nginx-proxy:
version: '3'
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
ports:
- '80:80'
- '443:443'
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- certs:/etc/nginx/certs:rw
- vhost.d:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
labels:
- com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt
depends_on:
- nginx-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- certs:/etc/nginx/certs:rw
- vhost.d:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
environment:
- NGINX_PROXY_CONTAINER=nginx-proxy
Database (MariaDB)
MariaDB as the database for Ghost.
We will use the image: mariadb:latest, we must configure the credentials and name of the database and persist the data on a volume:
version: '3'
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
ports:
- '80:80'
- '443:443'
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- certs:/etc/nginx/certs:rw
- vhost.d:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
labels:
- com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt
depends_on:
- nginx-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- certs:/etc/nginx/certs:rw
- vhost.d:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
environment:
- NGINX_PROXY_CONTAINER=nginx-proxy
mariadb:
image: mariadb:latest
container_name: mariadb
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=ghost
- MYSQL_USER=ghost
- MYSQL_PASSWORD=ghostpassword
volumes:
- mariadb-data:/var/lib/mysql
Ghost Service
Finally, we need to configure the Ghost service itself.
We will use the image: ghost:lates, we must indicate nginx-proxy and mariadb as dependencies to wait until we have already configured the SSL certificate and the DB before raising ghost, we must configure the following URL variables, the virtual host, the email for LetsEncrypt, and the database credentials, we need to create a new volume to store the data generated by the service:
version: '3'
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
ports:
- '80:80'
- '443:443'
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- certs:/etc/nginx/certs:rw
- vhost.d:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
labels:
- com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt
depends_on:
- nginx-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- certs:/etc/nginx/certs:rw
- vhost.d:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
environment:
- NGINX_PROXY_CONTAINER=nginx-proxy
mariadb:
image: mariadb:latest
container_name: mariadb
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=ghost
- MYSQL_USER=ghost
- MYSQL_PASSWORD=ghostpassword
volumes:
- mariadb-data:/var/lib/mysql
ghost:
image: ghost:latest
container_name: ghost
depends_on:
- nginx-proxy
- mariadb
environment:
- url=https://example.com
- VIRTUAL_HOST=example.com
- LETSENCRYPT_HOST=example.com
- [email protected]
- database__client=mysql
- database__connection__host=mariadb
- database__connection__user=ghost
- database__connection__password=ghostpassword
- database__connection__database=ghost
volumes:
- ghost-data:/var/lib/ghost/content
Volumes and network
Finally, we must define the volumes for data persistence and a default bridge type network.
Final content of the compose file
Here is the complete docker-compose.yml that describes the mentioned configuration:
version: '3'
services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
ports:
- '80:80'
- '443:443'
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- certs:/etc/nginx/certs:rw
- vhost.d:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
labels:
- com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt
depends_on:
- nginx-proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- certs:/etc/nginx/certs:rw
- vhost.d:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
environment:
- NGINX_PROXY_CONTAINER=nginx-proxy
mariadb:
image: mariadb:latest
container_name: mariadb
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=ghost
- MYSQL_USER=ghost
- MYSQL_PASSWORD=ghostpassword
volumes:
- mariadb-data:/var/lib/mysql
ghost:
image: ghost:latest
container_name: ghost
depends_on:
- nginx-proxy
- mariadb
environment:
- url=https://example.com
- VIRTUAL_HOST=example.com
- LETSENCRYPT_HOST=example.com
- [email protected]
- database__client=mysql
- database__connection__host=mariadb
- database__connection__user=ghost
- database__connection__password=ghostpassword
- database__connection__database=ghost
volumes:
- ghost-data:/var/lib/ghost/content
volumes:
ghost-data:
mariadb-data:
certs:
vhost.d:
html:
networks:
default:
driver: bridge
Running this docker-compose.yml with the docker-compose up -d command will raise all the services needed to run Ghost in a Dockerized environment, with SSL and database configured.
You would only have to configure your blog by entering the url that you configured within the service, in my case for the example it would be the following: https://example.com/ghost
Thanks for reading me ๐
Discussion