In order for our Ghost blog to talk to the web we will first need to setup the NGINX Reverse Proxy and the Lets Encrypt Companion containers using docker and docker-compose.
Before you begin
You'll need a VPS (Virtual Private Server). There are plenty of providers out there; I personally use Vultr and if you'd like to sign up consider using my referral link to support the blog and also earn yourself a $100 credit.
Previous part in the how-to series: How to choose and setup A VPS with Docker and Docker Compose for a Ghost Blog
Next Part in the how-to series: How to Setup A Ghost blog using MariaDB with NGINX and Lets Encrypt on Docker Compose
Setting up NGINX Reverse Proxy with SSL
The NGINX Reverse Proxy, which we're going to run in a Docker container is the main component that sits as the gatekeeper to the outside world. It's job is to listen on external ports 80 (HTTP) and 443 (HTTPS) and direct incoming requests to the correct container service without directly exposing what's going on inside your VPS.
The SSL companion Docker container, which we're going to be running in tandem with the NGINX reverse proxy automatically redirects all connections from HTTP to HTTPS and handles SSL encryption for all traffic, acting as a demarcation point. This container will also process certificate generation via the use of Letsencrypt for any domains that you start a docker container with automatically.
As you can see we're dockerising each and every different function that we require. For this part of the guide lets dockerise just the Reverse Proxy and the NGINX-SSL companion components
Lets first create our folder structure which will house the docker-compose files for the two services we're about to spin up on our VPS.
Login to your VPS and substitute your user for the one we created earlier.
Enter into the users home folder by typing
Next lets create a proxy folder
Inside the proxy folder we now need to create our docker-compose.yml file. This is a file that is written in Yaml which will define what docker containers we want to run.
version: '2' services: nginx-proxy: image: jwilder/nginx-proxy container_name: nginx-proxy ports: - "80:80" - "443:443" volumes: - conf:/etc/nginx/conf.d - vhost:/etc/nginx/vhost.d - dhparam:/etc/nginx/dhparam - certs:/etc/nginx/certs:ro - /var/run/docker.sock:/tmp/docker.sock:ro - /usr/share/nginx/html networks: - proxy restart: always letsencrypt: image: jrcs/letsencrypt-nginx-proxy-companion container_name: nginx-proxy-le volumes_from: - nginx-proxy volumes: - certs:/etc/nginx/certs:rw - /var/run/docker.sock:/var/run/docker.sock:ro restart: always volumes: conf: vhost: dhparam: certs: networks: proxy: external: name: nginx-proxy
Once you've copied this into the file press Ctrl+ O and then select Y to save the file. To exit the nano editor hit Ctrl + X.
lets go into a bit of detail in order to understand what's going on here. From a high level we're asking Docker to spin up two services, Nginx-proxy and Letsencrypt, define a few volumes and connect to a custom nginx-proxy external network.
- Version is the version of the docker-compose file formatting that we're using for this file, in this case we're using version 2
- The Image is the name of the executable package of software that includes everything we need in order to run an NGINX Reverse Proxy all contained within a nice file for us.
- We've defined the container_name as nginx-proxy to assist us in identifying what container is running what down the line.
- Ports 80 and 443 are forwarded onto the host (our VPS) in order to make the Nginx-proxy internet facing. No other containers will need their ports forwarded as this container handles all communication
- /var/run/docker.sock from the host is mounted allowing the proxy to listen in on other containers starting and stopping on the host which allows the container to dynamically update NGINX domain traffic forwarding to new containers that spin up or are deleted on the fly.
- Various NGINX directories are mounted on the host as named volumes to keep them persistent even if the docker instance were to be deleted and recreated and are listed as either as :rw (read/write) or :ro ?(read only)
- The container is assigned to the proxy external network, which provides it access to the host's internet facing IP address.
- The directive restart: always ensures that the container will always try and restart if you restart your VPS or it gets deleted for some reason.
Letsencrypt is the companion container to the nginx-proxy which handles all the necessary SSL activities including obtaining the required certificates from the Let's Encrypt service and auto-configuring the nginx-proxy service to route the encrypted traffic to the various containers you have running on your VPS dependent on the domain the connection is requesting.
- Letsencrypt it also connected to the /var/run/docker.sock in order for it to listen for new containers that are spun up or down and to get information required to generate the SSL certificates.
- It shares its volumes with the nginx-proxy using the volumes_from: nginx-proxy directive as all it has to do is update configurations that nginx-proxy is running.
- This container is not assigned to an external network
The External Network
There are a few caveats that you'll want to know about with networking on docker that are important to share as they gave me a headache when i did it for the first time:
When you create a docker-compose.yml file any services that you define as part of that file such as the Nginx-proxy and the Letsencrypt service that we've defined above will be automatically internally networked together. The reason why we are creating an external network is for the NGINX proxy to talk to the Host and receive and process requests for the domain (alexgallacher.com) to forward it on to our Ghost blog container.
In order for docker to use the "proxy" network you will need to create the network manually as this is not something docker-compose can handle using the following command:
sudo docker network create nginx-proxy
Once all this is complete we can try spinning up our docker-compose file.
Make sure you're in the location of the docker-compose.yml file before executing the docker-compose up command.
Lets execute the command to tell Docker to create our instances based on our docker-compose.yml file. This may take a while as it has to download, extract and install the docker images the first time it runs.
sudo docker-compose up
Appending -d will detach from the docker logs for the docker-compose.yml file.
sudo docker-compose up -d
Confirm the two docker containers are running by using the following command: