Introduction
After building a reverse proxy in a Docker container, the next step is to secure the connection between the outside and the reverse proxy using SSL via Let's Encrypt.
Created a minimal reverse proxy using Docker. (Memo)
There are several methods to issue and renew certificates with Let's Encrypt. The simplest one is the "Standalone" method, which temporarily stops running services. Certbot launches its own web server to perform the ACME challenge, requiring ports 80 and 443 and thus causing conflicts with running services.
To achieve a zero-downtime renewal, we use methods like HTTP-01 or DNS-01 challenges. This article uses the common HTTP-01 challenge via the Webroot method.
Certbot places the ACME challenge files into the webroot directory. Then Let's Encrypt tries to access http://<YOUR_DOMAIN>/.well-known/acme-challenge/<TOKEN>
. If a valid response is received, the certificate is issued.
https://letsencrypt.org/en/docs/challenge-types/
Important Note
Due to editor limitations, slashes in some paths are shown as full-width.
Please replace /etc
with standard slashes /etc
when copying the commands.
Using Certbot for HTTP-01 Challenge
This article covers up to the initial certificate issuance.
The structure is shown below:
Simplified here, but we will create reverse-proxy.yml
and webapp01.yml
using docker-compose, and launch each container. External requests to ports 80/443 are received by the reverse proxy, which forwards them to the web service in webapp01
based on the requested domain.
SSL is only applied between the external client and the reverse proxy.
Volumes are bind-mounted for the ACME challenge directory, certificate storage, and symbolic link paths.
Let’s walk through the setup steps.
Installing Certbot
Certbot is a tool for obtaining SSL certificates and supports Let's Encrypt.
Install the Certbot package:
sudo apt install certbot
Since we are using the Webroot method, additional plugins for web servers like Apache or Nginx are not needed.
Example: sudo apt install python3-certbot-nginx
(not required)
Add to docker-compose.yml for Authentication
First, create the directory for the ACME challenge.
Assume the project directory is /home/docker
.
Each docker-compose project has its own folder.
Since SSL is applied to the reverse proxy server, we configure it accordingly.
・/home/docker/reverse-proxy/docker-compose.yml
services:
reverse-proxy:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./conf.d:/etc/nginx/conf.d
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt
- ./certbot/conf/archive:/etc/letsencrypt/archive
The network and some configs are omitted for brevity.
Bind-mounted volumes:
./conf.d
→/etc/nginx/conf.d
: Nginx config files./certbot/www
→/var/www/certbot
: ACME challenge files./certbot/conf
→/etc/letsencrypt
: symbolic links for certificates./certbot/conf/archive
→/etc/letsencrypt/archive
: certificate storage
Create Directory for ACME Challenge
Next, create the directory:
/home/docker/reverse-proxy/certbot/www/.well-known/acme-challenge/
Configure Nginx
Place the reverse proxy config file (e.g., reverse-proxy.conf
) in /home/docker/reverse-proxy/conf.d
:
server {
listen 80;
server_name example(domain);
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
# other settings
}
}
Verify with --dry-run
Let’s Encrypt limits the number of real requests, so verify first with --dry-run
:
sudo certbot certonly --webroot -w /home/docker/reverse-proxy/certbot/www -d example(domain) --dry-run
Issue the Certificate
Use the following command to issue the certificate (example domain):
sudo certbot certonly --webroot -w /home/docker/reverse-proxy/certbot/www/ -d example(domain)
Make sure there’s no error and the certificate is stored at the specified location.
Conclusion
It took quite a bit of time to get this working.
When running the issuance command, I received a 404 error. Although I could access test .html
files placed under /.well-known/acme-challenge/
, the error persisted.
I tried tweaking autoindex on;
, default_type text/plain;
, and permissions settings.
In the end, the problem was that I specified the container's directory in the Certbot command instead of the host’s directory. Certbot runs on the host, so it needs to reference the host’s path.
Reference
https://letsencrypt.org/en/docs/challenge-types/
Related Articles
How to enable HTTPS with HTTP-01 Challenge using Docker Compose Reverse Proxy
Automating SSL updates for custom domains on Sakura VPS using Docker Compose Reverse Proxy