Today’s most web applications probably have these characteristics:
- they are complex
- as such, they depend on many moving pieces (e.g. reverse proxy, cache, db, etc)
- require secure connections via TLS
Here are quick tips I found useful to deploy such applications.
Technologies used:
- Docker
- Docker Compose
- Docker Machine
- Nginx
- LetsEncrypt (with tool called
certbot
)
Docker Compose is extremely useful to help manage the complexity of the application’s moving pieces. In case you did not use it before, here is my 2 second pitch. Docker Compose allows to define all of the components in a single configuration therefore allowing for easier maintenance and deployment.
For most use-cases the public-facing component of the application will probably be a reverse proxy. Nginx is one of the most popular reverse proxy servers out there. It is really reliable and lightweight. It often uses <5Mb memory. I’m not sure you can ask for more.
Since stateless applications are cool (12 Factor at all that jazz), nginx should be build as a separate docker compose service. In order words, instead of mounting a volume into the nginx container with the nginx configuration, the container should be built with the configurations baked in. That will make nginx container portable. The resulting Dockerfile will be something like:
Then it can simply be used within the docker-compose.yml
:
Now the application can be deployed anywhere with Docker Compose in combination with Docker Machine:
This is where things get more interesting. In order to configure TLS in nginx container, we need to have certificate installed in the container as well. Copying site’s certificate private key into the container does not seem like a good idea though. The fact that LetsEncrypt issues certificates for only 90 days is great for security but does not help in convenience department. So the question is how to do that without adding too much complexity.
Docker Volumes come to the rescue. Volumes allow to manage data attached to a container independently from the container. That means couple of things:
- we can put certificate information on a separate volume which will only be accessible from nginx container
- that volume will only have a single copy - in prod - so we will never touch the certificate in dev machines
Adjusted docker-compose.yml
will be something like:
After volumes are configured, we need to adjust nginx settings in order to make it compatible with LetsEncrypt process.
LetsEncrypt is a free certificate authority which automates the process of creating certs for a site.
Its used via a certbot
tool.
Now that nginx is configured to server ACME challenges from .well-known
directory, we can request certificate from LetsEncrypt:
Above command will use ACME protocol to issue the certificate for example.com
.
While doing that it will validate that we are in control of example.com
by storing some files in .well-known
which nginx will serve.
If successfully validated, certificate for example.com
will be placed in /etc/letsencrypt
which is inside certs
volume.
Since Nginx will have access to the same certs
volume, we can now configure nginx to actually serve the site over TLS:
Above config is btw what I use for my sites which scores A+
in SSLTest.
After a couple of months, the certs can be easily renewed:
Alternatively cronjob can be used on the host machine to do that automatically every 15 days or so:
Thats it! Its pretty much everything necessary to run site with docker + nginx + LetsEncrypt.
Side Note:
LetsEncrypt is on a mission to encrypt the whole web so if are able to pitch in financially to them, please consider that.