I like nginx. I like docker. I like SSL. I also like not paying for SSL certificates. If you, like me are operating a small time website, you probably don’t want to dish out a few hundred dollars for an SSL certificate.Thankfully, LetsEncrypt provides free ssl certificates and is now trusted by most major browsers. However, integrating certbot to automatically configure my nginx inside a docker container has been a pain. In the past, I generated the standalone cert with –cert-only and then linked my docker container to use it but this was a far from usable solution given the horrid automation code I had to write. Recently, I tried to come up with a more automation friendly solution and ended up with the following:
-
Custom nginx image with certbot pre-installed inside the container.
Dockerfile:
FROM nginx RUN apt-get update && apt-get install -y \ certbot \ python-certbot-nginx ADD certify.sh / RUN chmod +x certify.sh
certify.sh:
#!/bin/bash certbot --nginx -d $DOMAIN -m $EMAIL --agree-tos -n --redirect
Github-Source: https://github.com/tchaudhry91/nginx-certbot-docker DockerHub Image: https://hub.docker.com/r/tchaudhry/nginx-certbot-docker/
-
Then I would simply run the container with the following docker-compose block:
version: '3' services: web: image: tchaudhry/nginx-certbot-docker container_name: web ports: - "80:80" - "443:443" environment: - DOMAIN=example.com - EMAIL=xyz@example.com volumes: - letsencrypt:/etc/letsencrypt - ./config/nginx_config.conf:/etc/nginx/conf.d/default.conf volumes: letsencrypt:
An equivalent
docker run
command would also suffice. But do make note of the enviroment variables and the letsencrypt volume.- The enviroment variables are used to provide information about the domain for which the certificate is to be requested.
- The letsencrypt volume is also essential as this will prevent recertification till the old certificate expires. This can also be a host-mounted directory, however I prefer docker volumes.
-
This image does not do ssl yet. Infact, it can be used as a drop in replacement for the regular nginx image. I can ‘certify’ the container inside it on demand with:
docker exec container_name /certify.sh
This command can easily be cron-ed on the base system to re-certify and prevent expired certificates.
Pitfalls
- While this works for now, a true service is most likely going to be load balanced. This approach would require scaling down a single container to ensure the letsencrypt verification passes before certification. The service can then be scaled back up, and given the shared volume, it will work without requiring re-certification.