
How to set HTTPS with Certbot for Nginx on Docker
Certbot is an awesome tool to help you enable HTTPS with Let's Encrypt. It provides several instructions guiding you how to setup HTTPS. However I was blocked when I run it with the following command because... my nginx is running on docker, the cerbot
failed to find the nginx instance.
sudo certbot --nginx
After some investigation, I figured out a way to set up dockerized nginx with certbot.
Docker compose and bind mount
- create two empty directories on the docker host at the same directory with the docker-compose file:
./certbot/www
and./certbot/conf
- suppose the nginx has been already set up in the docker-compose file, add the certbot together with two volumes as follows,
services:
nginx:
build: ./nginx_home
container_name: nginx
ports:
- "80:80"
networks:
- webapp
restart: always
volumes:
- ./certbot/www/:/var/www/certbot/:ro
- ./certbot/conf/:/etc/nginx/ssl/:ro
certbot:
image: certbot/certbot:latest
volumes:
- ./certbot/www/:/var/www/certbot/:rw
- ./certbot/conf/:/etc/letsencrypt/:rw
Explanations:
- The
certbot
will store the certificates under the directory/etc/letsencrypt
which is mapped to the./cerbot/conf
on host, on the other hand,.certbot/conf
is also mounted to/etc/nginx/ssl
directory onnginx
container. - The other shared directory
./certbot/www
is used for the HTTP-01 challenge. Once you get a certificate,Let's encrypt
validates that you control the domain names withchallenges
, in a nutshell, it issues a token then verifiy it with a http request to your web server like below,
So, to pass the challenge the nginx config file also needs to be updated to expose the token,
server {
listen 80;
listen [::]:80;
server_name [YOUR_DOMAIN]
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
...
}
Run the certbot
Use docker compose run
to obtain a certificate, you'd better to apply with --dry-run
for a test first as Let's Encrypt limits the available free cerfitificates per month.
docker compose run --rm certbot \
certonly --webroot --webroot-path /var/www/certbot/ --dry-run -d [YOUR_DOMAIN]
You probably need to provide your email address and consent the Terms of Service.
if everything goes well you can run the above command without the --dry-run
flag.
Configure the certificates with Nginx
Now you should have your certificates under the directory ./certbot/conf/live/[YOUR_DOMAIN]
, it's time to configure the nginx with the certificates as follows,
and it's better to add a 301 redirect to prevent the http access.
server {
listen 80;
listen [::]:80;
server_name [YOUR_DOMAIN]
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://[YOUR_DOMAIN]$request_uri;
}
...
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name [YOUR_DOMAIN];
ssl_certificate /etc/nginx/ssl/live/[YOUR_DOMAIN]/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/live/[YOUR_DOMAIN]/privkey.pem;
location / {
proxy_pass http://webapp:3000;
}
...
}
Renew certificates
Let's Encrypt certificates only last for three month, to renew it, run the following command,
docker compose run -rm certbot renew
Side notes
- Don't forget to add the 433 listening in the nginx config
- Don't forget to enable the firewall of your server before testing the https.
- You can test the certificates with
openssl s_client -connect [YOUR_DOMAIN]:443 -showcerts
- If you need to expand the certificate's domain, use
certbot certonly --cert-name example.com --webroot --webroot-path /var/www/certbot/ -d example.com,www.example.com