Many people have tackled the problem of issuing Let's Encrypt SSL certificates for an Nginx webserver in a Docker environment. Many of these solutions are great but can take a fair amount of time to complete.
Instead of writing lengthy instructions, I created a script that automates most of the process for you. Your mother warned you against downloading scripts from strange men on the internet and so I have made all the source code and documentation available on GitHub.
Of all the guides and tutorials on the web, I'm going to be outrageously bold and say that mine is the quickest and easiest.
docker
and docker compose
installed.curl -OO https://raw.githubusercontent.com/joncombe/docker-nginx-letsencrypt-setup/main/{(certbot.json, certbot.py)}
certbot.json
file. You likely only need to change the domain
and email
fields, but do check the documentation. The email is passed to Let's Encrypt as part of the certificate creation process, I never see it.python3 certbot.py
The script has fetched and installed an SSL certificate for you from Let's Encrypt. Go to your (sub)domain in your browser to see it working. Awesome!
If you now list the contents of your folder you will see you have one new folder and two new files. Your certificates are stored in the data
folder.
> ls data docker-compose.yml nginx.conf
Think of the docker-compose.yml
as a starting template for your new project. This is your file to add to and edit as you please, but just be careful with the volumes
mappings within the webserver
and certbot
services (shown here in blue). You can add other volumes of course, but be sure to keep these ones.
version: '3' services: webserver: image: nginx:1.23.4-alpine ports: - 80:80 - 443:443 restart: always volumes: - ./data/certbot/conf/:/etc/nginx/ssl/:ro - ./data/certbot/www:/var/www/certbot/:ro - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro certbot: image: certbot/certbot:latest volumes: - ./data/certbot/conf/:/etc/letsencrypt/:rw - ./data/certbot/www/:/var/www/certbot/:rw
Your nginx.conf
file will look like the one below exceptexample.com
will be replaced with your (sub)domain.
The first server
block redirects HTTP traffic to HTTPS. You probably don't need to touch this.
The second server
block is just a starting point: you will need to add your own rules here. Nginx configurations vary by use case and can get very complex, so it is not my place to dictate what is in your config file, however, be sure to keep the ssl_certificate
and ssl_certificate_key
lines exactly as they are (highlighted here in blue).
server {
listen 80;
listen [::]:80;
server_name _;
server_tokens off;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://example.com$request_uri;
}
}
server {
listen 443 default_server ssl http2;
listen [::]:443 ssl http2;
server_name _;
server_tokens off;
ssl_certificate /etc/nginx/ssl/live/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/live/example.com/privkey.pem;
location / {
if ( $host !~* ^(example.com)$ ) {
return 444;
}
root /usr/share/nginx/html;
}
}
You need to add two new cronjobs on the server. The first line instructs the Certbot docker container to request a new certificate before the old one expires. The second line reloads nginx (with zero downtime) so that it discovers the new certificate.
Important: which cronjobs you use depends on which version of docker compose you have installed.
If you use the docker compose
command - without a hyphen - add these lines to your crontab:
0 0,12 * * * docker compose run --rm certbot renew 5 0 * * * docker exec webserver nginx -s reload
If you use the legacy docker-compose
command - with a hyphen - add these ones instead:
0 0,12 * * * /usr/local/bin/docker-compose run --rm certbot renew 5 0 * * * docker exec webserver nginx -s reload
Yup, that's it. If you have any problems please double check the instructions here and in the GitHub repo. If that doesn't fix it, please raise an issue or drop me a line. If this saved you a chunk of time and hair-pulling, I'd like to think this repo is worthy of a GitHub star.
This method has been used and tested on Debian/Ubuntu-based servers. Feedback from users of other Linux distros would be gratefully received.
Happy secure browsing!