DEV Community

Daniel Imfeld
Daniel Imfeld

Posted on • Originally published at imfeld.dev on

Setting up Let's Encrypt with NGINX in Docker

I was setting up Let’s Encrypt today for the first time, and it’s impressive how easy they’ve made this process that used to be a big hassle. Our NGINX configuration at work is too complicated for certbot’s built-in configuration management, and so we write the configuration ourselves.

NGINX Configuration

First, you need to respond to the ACME request on non-secure HTTP. This configuration does that while redirecting everything else to HTTPS.

server {
  listen 80;
  server_name _;
  # The directory that certbot will use to respond to the ACME challenge
  root /var/www;

  location /.well-known/acme-challenge {
    try_files $request_uri =404;
  }

  location / {
    rewrite ^ https://$host$request_uri? permanent;
  }
}

Enter fullscreen mode Exit fullscreen mode

After that, you can set up your server as normal, but one problem remains…

SSL Certificate Bootstrapping

You don’t have the SSL certificates yet, and NGINX won’t start if they aren’t present.

This would present a chicken-and-egg problem, but we can supply a fake chicken in the form of a self-signed certificate.

Browsers will reject this, but that’s ok. All we really need to do is get NGINX to start so it can serve the port 80 ACME requests, then certbot will be able to function.

You could also just comment out the server block that needs the SSL certificate, but that is more of a hassle IMO.

Since I always forget how to generate a simple self-signed SSL cert, here’s one method:

openssl req -nodes -batch -x509 -newkey rsa:2048 \
  -keyout privkey.pem -out fullchain.pem -days 360
Enter fullscreen mode Exit fullscreen mode

Then you can copy the resulting files into the proper location in /etc/letsencrypt/live/$CERTNAME/, and start NGINX.

Now, certbot doesn’t really like if there’s some fake self-signed certificate in the directory it’s expecting to use, so once NGINX is started you’ll want to delete the files you just created.

sudo rm -i /etc/letsencrypt/live/$CERTNAME/*.pem
sudo rmdir /etc/letsencrypt/live/$CERTNAME
Enter fullscreen mode Exit fullscreen mode

Finally, you can run certbot certonly --nginx --cert-name $CERTNAME -d $DOMAIN to generate the certificate.

NGINX inside Docker

If you’re running NGINX inside a Docker container, you should use certbot’s webroot method instead of the nginx method.

certbot certonly \
  --webroot -w /var/www \
  --key-type ecdsa \
  --cert-name $CERTNAME \
  -d $DOMAIN
Enter fullscreen mode Exit fullscreen mode

In this case you’ll also need to tell the NGINX in the docker container to reload, and certbot makes this easy with post-renewal hooks.

Create the file /etc/letsencrypt/renewal-hooks/post/nginx-docker-reload.sh with these contents:

#!/bin/bash
set -e
docker exec -it nginx nginx -s reload
echo Reloaded nginx!
Enter fullscreen mode Exit fullscreen mode

You can test this script by running sudo certbot renew --dry-run.

If you need to copy the SSL certificates from /etc/letsencrypt/live/$CERTNAME to somewhere else that the Docker container can see, you can also do it in that hook.

Alternative Methods

I should note that ACME/LetsEncrypt/certbot can also work via DNS records. For this particular case that was not an option, but it could make things easier, and certbot has a number of plugins to interface with various DNS providers.

Top comments (0)