Let's Encrypt Something
We want the internet to be confident who we are. As shown in the Certificate Authority design, we have our own Certificate Authority. But now we want to get our keys signed by the Let's Encrypt service. This image illustrates how it works:

The Root Certificate Authority is a service run by Let's Encrypt, and it will automatically sign certificates using its own API. These certificates last three months, at which time you will need to fetch a fresh one.
The certificate client is our own web server (the NGINX Docker container), which needs to be visible on the Internet on both HTTP and HTTPS. This functionality will be provided by our own NGINX Docker image running on the webserver. The web server will provide the HTML content. There are two sets of information: the actual content of your website, which you write yourself, and the ACME handshake. This is a randomly numbered subdirectory under your webserver. It contains the evidence that the Certbot asking for the certificate is really the one controlling that website. Let's Encrypt will try to access that on the URL provided using HTTP on port 80 of the URL provided.
So every month or so, our Certbot will wake up and:
- Generate a new private key for the website.
- Generate a Certificate Signing Request (CSR) from this key.
- Send the CSR to Let's Encrypt over their API.
- Let's Encrypt will send CertBot a challenge:
- Please put this information into www.example.com and name it: 1234/5678
- Certbot will put the information where it is supposed to go and acknowledge this to Let's Encrypt
- Let's Encrypt will try to access the file
- If it finds what it is expecting, let's Encrypt will send a new certificate to CertBot
- CertBot will put the certificate in the NGINX configuration
And then, certified access to the website can continue.
References
- EFF: Certbot Command line parameters
- Let's Encrypt: Let's Encrypt how it works
- Readthedocs.io: Documentation for Docker Certbot/certbot image
- Dockerhub: Docker container for Certbot
Certbot configuration
We have the webserver running in a separate docker container with its certificate and content directories imported from the host. Hence, the Certbot docker image will not configure NGINX for us. We will be using Certbot using the "webroot" method: It will put the responses to the challenges it receives into the HTML directory when asked. In our case. the web data is in /local/www/, and the challenge will be stored in /local/www/.well-known/acme-challenge/$RANDOM_STRING.
The certbot command to achieve this is:
certbot certonly --noninteractive --webroot \
--cert-name www-nerdhole \
--webroot-path /local/www \
--domain www.nerdhole.me.uk \
--domain nerdhole.me.uk
After that, we need to reload NGINX to make it use the new certificate.
Docker container configuration
We will be using the certbot/certbot:latest image from Docker Hub. We do not want to use the snap image to keep software management on the main server simple. This image uses a number of directories containing important files:
| Host | Container | Contents |
|---|---|---|
| /local/www | /local/www | Root directory for the web server, also used by the NGINX container |
| $DOCKER_HOME/apps/www-nerdhole/etc/letsencrypt | /etc/letsencrypt | Let's Encrypt configuration files |
| $DOCKER_HOME/apps/www-nerdhole/var/lib/letsencrypt | /var/lib/letsencrypt | Run files for Let's Encrypt |
| $DOCKER_HOME/apps/www-nerdhole/var/log/letsencrypt | /var/log/letsencrypt | Log files for Let's encrypt |
Because we want to look at these files directly, we will mount the directories under the Docker Home, here shown as $DOCKER_HOME, which is /local/docker. The certificates are stored in /etc/letsencrypt/live/$domain/. We will name the certificates after the Nerdhole website: www-nerdhole.
Let's Encrypt Subscription
You need a subscription with Let's Encrypt to get certificates. The first time it is run, Certbot will ask you to subscribe. It will also ask you to accept a license agreement. We will run this by hand once, using the following command:
docker run -it --rm --name certbot \
-v "/local/www:/local/www" \
-v "/local/docker/apps/www-nerdhole/vol/etc/letsencrypt:/etc/letsencrypt" \
-v "/local/docker/apps/www-nerdhole/vol/var/lib/letsencrypt:/var/lib/letsencrypt" \
-v "/local/docker/apps/www-nerdhole/vol/var/log/letsencrypt:/var/log/letsencrypt" \
certbot/certbot certonly \
--webroot \
--webroot-path /local/www \
--cert-name www-nerdhole \
--domain www.nerdhole.me.uk \
--email certs@nerdhole.me.uk
Since our provider does not allow us to specify an A record for nerdhole.me.uk, we will only register www.nerdhole.me.uk. The first time Certbot is run, it gave me this output:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at:
https://letsencrypt.org/documents/LE-SA-v1.6-August-18-2025.pdf
You must agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: n
Account registered.
Next, it tried to create the certificate.
Useful information
Code snippets:
docker run -it --rm --name certbot \
-v "/local/www:/local/www" \
-v "/local/docker/apps/www-nerdhole/vol/etc/letsencrypt:/etc/letsencrypt" \
-v "/local/docker/apps/www-nerdhole/vol/var/lib/letsencrypt:/var/lib/letsencrypt" \
-v "/local/docker/apps/www-nerdhole/vol/var/log/letsencrypt:/var/log/letsencrypt" \
certbot/certbot certonly \
--webroot \
--webroot-path /local/www \
--cert-name www-nerdhole \
--domain www.nerdhole.me.uk \
--domain nerdhole.me.uk \
--email certs@nerdhole.me.uk
sudo certbot renew --dry-run