the cover image of blog post How to set HTTPS with Certbot for Nginx on Docker

How to set HTTPS with Certbot for Nginx on Docker

2025-01-19
7 min read

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 on nginx 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 with challenges, in a nutshell, it issues a token then verifiy it with a http request to your web server like below,

image

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
© 2025 Xavier Zhou