If you have ever renewed an SSL certificate by hand at midnight because a site went down, you already know why you should automate SSL certificates in Docker. Manual renewals are easy to forget, and a single missed one means a broken padlock icon and lost trust.
The good news is that with Certbot, Nginx, and a small Docker Compose setup, free SSL certificate renewal can run forever without you touching the server again.
This guide walks through a self-hosted, container-based approach that avoids paid services entirely. By the end, you will have a Certbot container that checks for renewals automatically, a reverse proxy that serves the challenge files, and a repeatable setup you can drop onto any new VPS.
[!TIP]>— Run Certbot in its own Docker container using the webroot method, share a folder with your Nginx container for the ACME challenge, and let a renewal loop handle everything on a schedule. No cron jobs on the host, no manual Certbot commands, and no downtime.
Why Automate SSL Certificate Renewal in the First Place
Let's Encrypt certificates are free, but they only last 90 days. That short lifespan is a security feature, not an inconvenience, because it limits the damage if a key ever leaks. The tradeoff is that renewal has to happen reliably, on time, every time.
Doing this manually across multiple domains does not scale, especially once you are running several projects behind one reverse proxy.
Running Certbot directly on the host also creates a hidden dependency: if your Nginx setup moves into Docker later, host-based Certbot plugins that expect to control a system service often break. Containerizing Certbot from the start keeps your entire stack—proxy, certificates, and renewal logic—portable and easy to redeploy.
1. Set Up a Shared Webroot Folder
The webroot method proves domain ownership by placing a small file at a predictable URL, which Let's Encrypt then checks. Both Nginx and Certbot need access to the same folder for this to work.
mkdir -p ./certbot/www
mkdir -p ./certbot/conf
Mount both paths into your Nginx service so it can serve the challenge files, and add a matching location block so requests are routed correctly.
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
2. Add a Dedicated Certbot Container
Instead of installing Certbot on the host, run it as its own service that shares the certificate volume with Nginx. This keeps renewal logic isolated and easy to move between servers.
services:
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
entrypoint: >
/bin/sh -c "trap exit TERM; while :; do
certbot renew --webroot -w /var/www/certbot --quiet;
sleep 12h; done"
networks:
- my-network
This loop checks for renewals every twelve hours, which is well within Let's Encrypt's recommended polling frequency, and it only reissues a certificate when one is actually close to expiring.
3. Issue and Verify Your First Certificate
With the containers running, request a certificate the same way you would with host-based Certbot, just throughdocker exec instead.
docker exec certbot certbot certonly \
--webroot -w /var/www/certbot \
-d example.com \
-d www.example.com
Always test renewal logic with a dry run before trusting it in production. This simulates the entire process without touching your real certificate or hitting Let's Encrypt's rate limits.
docker exec certbot certbot renew --dry-run
[!WARNING]If you already had Certbot installed on the host, disable its cron job or systemd timer before switching to the container. Two renewal processes fighting over the same certificate files will cause lock errors and confusing failures.
4. Reload Nginx Automatically After Renewal
A renewed certificate does nothing until Nginx picks it up. Rather than reloading manually, add a lightweight reload loop to the Nginx service itself so it periodically re-reads its configuration and certificate files.
while :; do
sleep 6h & wait $!
nginx -s reload
done &
nginx -g 'daemon off;'
This approach means certificate rotation and configuration reloads happen entirely inside Docker, with no host-level scripts required. It also travels cleanly: copy the Compose file and Nginx configs to a new VPS, restore your certificate folder if you are migrating existing domains, and everything works the same way on day one.
Wrapping Up
Automating SSL certificates in Docker removes one of the most common causes of unexpected downtime, and it does so without paying for a managed certificate service. Once the Certbot container and shared webroot are in place, renewal becomes something that simply happens in the background, forever, for free.
From here, consider scripting certificate backups to off-server storage so a full VPS migration takes minutes instead of hours.



