Self-hosted service
JonasAlfredsson/docker-nginx-certbot avatar
JonasAlfredsson/docker-nginx-certbot

docker-nginx-certbot: Let's Encrypt Certificates For an Nginx Container

Automatically create and renew website certificates for free using the Let's Encrypt certificate authority.

1,208 stars195 forksShellMIT

At a glance

What is it?
docker-nginx-certbot is a container image that wraps the official Nginx images with certbot, so a container obtains and renews TLS certificates for your domains on its own. It handles multiple server names, wildcards over DNS challenge, RSA and ECDSA at the same time, and live config reloads through a SIGHUP.
Who is it for?
docker-nginx-certbot fits a self-hosted site running Nginx in Docker where you want certificates handled inside the same image as the web server, without a separate certbot container and a shared volume dance. It is a poor fit if you need certificates for a service that is not Nginx, or if you cannot expose port 80 to the public internet, since the default webroot authenticator needs it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Nginx and Certbot in the Same Container

docker-nginx-certbot automatically creates and renews website SSL certificates using the Let's Encrypt certificate authority and its client, certbot. It is built on top of the official Nginx Docker images, both the Debian and the Alpine ones, and it uses OpenSSL or LibreSSL to create the Diffie-Hellman parameters used during the initial handshake of some ciphers.

That last point is the difference between this and a plain certbot sidecar. A separate certbot container writes the certificate to a shared volume and Nginx has to be told to pick it up and reload. Here, the certificate and the web server are in the same container, and the image uses the parent container's /docker-entrypoint.d/ folder to hook its scripts into the standard Nginx startup sequence.

The behaviour details are the ones that matter in practice. The image will report the correct exit code when it is stopped, killed or failed, so an orchestrator does not see a clean shutdown after a failure. It can do a live reload of the configuration by sending in a SIGHUP signal, with no container restart needed. And it can be used offline with the help of a local certificate authority, which is what makes it usable in a development environment that has no public DNS.

The image is published as jonasal/nginx-certbot on Docker Hub, and the repository is MIT licensed and written as shell scripts under src/ with a tests/ directory.

The Environment Variables Decide the Cryptography

One variable is required and the rest shape what you get.

CERTBOT_EMAIL is the required one, and it is your email address, used by Let's Encrypt to contact you in case of security issues. Everything else has a default.

The cryptographic choices are three. RSA_KEY_SIZE sets the size of the RSA encryption keys and defaults to 2048. ELLIPTIC_CURVE sets the size or curve of the ECDSA keys and defaults to secp256r1. DHPARAM_SIZE sets the size of the Diffie-Hellman parameters and defaults to 2048. And USE_ECDSA, which defaults to 1, switches certbot to RSA instead of ECDSA when set to 0.

The operationally important one is STAGING, which defaults to 0 and, when set to 1, uses the Let's Encrypt staging servers. The documentation points at the initial testing section, and the practical reason is rate limits: the production certificate authority limits how many certificates you can request, and a container that retries a broken configuration will burn through that limit quickly. Test on staging until the whole flow works, then switch.

RENEWAL_INTERVAL is the time interval between certbot's renewal checks and defaults to 8d, which is how the periodic renewal is triggered rather than a separate cron.

Multiple Names, Wildcards and Two Certificates at Once

Three features cover the cases where a single certificate for a single host is not enough.

Multiple server names are handled when requesting certificates, so both example.com and www.example.com can be in one request. The documentation has a section on how the script adds domain names to certificate requests.

Wildcard domains are supported when you use DNS authentication, which is the only way a wildcard can work, since it requires adding a DNS record rather than answering a request on port 80. The relevant variables are CERTBOT_AUTHENTICATOR, the authenticator plugin to use when responding to challenges, which defaults to webroot, CERTBOT_DNS_CREDENTIALS_DIR, the directory where credentials for DNS authenticators should be located, defaulting to /etc/letsencrypt, and CERTBOT_DNS_PROPAGATION_SECONDS, the number of seconds to wait for the DNS challenge to propagate, which by default is certbot's own default.

The propagation wait is the setting people get wrong. Set it too low and the validation fails intermittently, and the failure looks like a certificate authority problem rather than a timing problem.

Both RSA and ECDSA certificates can be requested at the same time, which the documentation covers under a multi-certificate setup, and the example configuration files in the examples/ directory include one named example_server_multicert.conf for exactly that case. The remaining examples cover a basic server configuration and one with overrides.

The First Start Is Slow, and That Is Expected

The README puts a warning near the top, and it is the thing that most often looks like a broken image.

The very first time the container is started it might take a long time before it is ready to respond to requests, and the README links that to the Diffie-Hellman parameters section of the documentation. Generating DH parameters is deliberately slow, because the parameters have to be derived from a large prime, and the process is bound to be slow on a small machine or with a large DHPARAM_SIZE. A deployment that health checks the container during that window will restart it, and restarting it can restart the generation.

The other operational detail in the same area is the volume. The /etc/letsencrypt directory stores the obtained certificates and the Diffie-Hellman parameters, and it has to be a volume, otherwise the certificates are lost when the container is replaced and every renewal starts from scratch.

The run command in the README shows the shape:

bash
docker run -it -p 80:80 -p 443:443 \
           --env [email protected] \
           -v $(pwd)/nginx_secrets:/etc/letsencrypt \
           -v $(pwd)/user_conf.d:/etc/nginx/user_conf.d:ro \
           --name nginx-certbot jonasal/nginx-certbot:latest

The custom server configuration goes in the user_conf.d/ folder, and the documentation describes that folder separately.

Reloading Configuration With a Signal

Once the container is running, changing a configuration file does not require a restart, and the mechanism is a signal rather than a command.

The README says that if you change any of the config files after the container is ready, you can just send in a SIGHUP to tell the scripts and Nginx to reload everything, and the advanced usage documentation covers manual and forced renewal under the same heading.

bash
docker kill --signal=HUP <container_name>

The distinction between this and a restart is not only speed. The certificates and the DH parameters live in the /etc/letsencrypt volume, so a restart is not as expensive as it would be for a stateless container, but the DH parameters do not have to be regenerated because they are already on disk. A SIGHUP skips even that and leaves the running process in place, which is the difference between a configuration change that takes effect in a second and one that takes a container down and up.

It is also the correct way to trigger a forced renewal after changing something that affects the certificate request, for example adding a new domain name to the server configuration, since the certificate cannot change until certbot runs again.

Working Offline With a Local Certificate Authority

The offline capability is what makes this image usable in a development environment, and it is driven by a single variable.

USE_LOCAL_CA, which defaults to 0, enables the use of a local certificate authority, and the advanced usage documentation has a section describing it. The certificates produced this way are not trusted by any browser, but they are structurally real certificates, which is the point: your application makes the same TLS handshake, the same hostname verification and the same certificate handling as it will in production.

That matters more than it first sounds. A development stack that uses plain HTTP does not exercise the redirect rules, the HSTS headers, the mixed content errors or the certificate path handling that a real deployment hits, and those are exactly the problems that appear only after you go live.

DEBUG, defaulting to 0, is the other variable in this area. Setting it to 1 enables debug messages and uses the nginx-debug binary instead of the standard one, which is how you get a verbose picture of what the startup scripts are doing when a configuration is not behaving as you expect.

Fork History, Image Tags and How to Run It Under Compose

The lineage of the project explains why the configuration is shaped the way it is. The repository was originally forked from @henridwyer, then forked again by @staticfloat, before being forked once more by the current author. The README says the changes to the code have since become so significant that it has now been detached as its own independent repository, while still retaining all the history, and that migration instructions from the @staticfloat image are in the documentation.

The tag guidance matters in practice. The README asks you to use a specific tag when doing a Docker pull, since:latest might not always be 100 percent stable, and points at a documentation page listing the available tags. The images are built for both Debian and Alpine, and for multiple architectures, with the topics naming armv7 among them.

Under Docker Compose the shape is the same but the configuration is split in two files. There is an example docker-compose.yml in the examples/ directory, and the default parameters are in nginx-certbot.env, with the note that the defaults in the env file are overwritten by any environment variables set inside the yaml file. You can use both environment: and env_file: together or only one of them, and the only requirement is that CERTBOT_EMAIL is defined somewhere.

The release history is short and recent, with v6.2.0 and v6.1.0 both published on 2026-04-02 and v6.0.0 on 2025-06-18. The repository is not archived and the last push was on 2026-10-19.

Editorial conclusion

docker-nginx-certbot fits a self-hosted site running Nginx in Docker where you want certificates handled inside the same image as the web server, without a separate certbot container and a shared volume dance. It is a poor fit if you need certificates for a service that is not Nginx, or if you cannot expose port 80 to the public internet, since the default webroot authenticator needs it. Before deploying, set STAGING to 1 and use the Let's Encrypt staging servers until the setup works, because the documentation points out that the production servers rate limit, and remember that the very first start can take a long time while the Diffie-Hellman parameters are generated. The image is MIT licensed, the newest release is v6.2.0 from 2026-04-02, and the last push was on 2026-10-19.

Frequently asked questions

What does docker-nginx-certbot do?

It automatically creates and renews website SSL certificates using the Let's Encrypt certificate authority and certbot, built on top of the official Nginx Docker images for both Debian and Alpine. It also creates the Diffie-Hellman parameters used during the initial handshake of some ciphers.

Which environment variables are required and which are optional?

CERTBOT_EMAIL is the only required one, and it is used by Let's Encrypt to contact you about security issues. The optional ones include DHPARAM_SIZE, ELLIPTIC_CURVE, RENEWAL_INTERVAL, RSA_KEY_SIZE, STAGING, USE_ECDSA, CERTBOT_AUTHENTICATOR, CERTBOT_DNS_PROPAGATION_SECONDS, CERTBOT_DNS_CREDENTIALS_DIR, DEBUG and USE_LOCAL_CA.

How do I reload the configuration without restarting the container?

Send in a SIGHUP signal, with `docker kill --signal=HUP <container_name>`. The README says this tells the scripts and Nginx to reload everything, and the advanced usage documentation covers manual and forced renewal under the same heading.

Can this image issue wildcard certificates?

Yes, when you use DNS authentication. The relevant variables are CERTBOT_AUTHENTICATOR, CERTBOT_DNS_CREDENTIALS_DIR for the credentials location and CERTBOT_DNS_PROPAGATION_SECONDS for how long to wait for the challenge to propagate.

Why is the container slow to start the first time?

Because it generates the Diffie-Hellman parameters, which is deliberately slow. The README warns that the first start might take a long time before the container is ready to respond to requests, and the documentation has a section on Diffie-Hellman parameters.

Official sources

  1. JonasAlfredsson/docker-nginx-certbot on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jonasalfredsson-docker-nginx-certbot.svg)](https://hysenlabs.com/projects/jonasalfredsson-docker-nginx-certbot)

Was this useful?