Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

You can renew certificates for several domains served by Nginx in Docker on an EC2 instance without manual steps, and have Nginx load the new files without a container restart. That takes four deliberate choices: certificate and configuration files live on the host instead of inside the container, the ACME challenge can be answered (on port 80 for HTTP-01, or through an automated DNS API for DNS-01), renewal runs on a schedule, and a deploy hook checks the Nginx configuration before it sends the reload signal.

Nginx’s documented graceful reload is the mechanism that makes this practical. It reduces disruption; it does not guarantee that no request will ever fail. The steps below are a configuration to verify in staging, not a recipe that has been run end to end on a live EC2 instance.

How the pieces fit together

The renewal path has six links, and a failure at any one of them leaves the old certificate in service:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Each public hostname resolves to the EC2 instance (or to whatever sits in front of it).
  • Nginx has a server block for each hostname and points at a certificate file for it.
  • Certbot obtains and stores certificates in /etc/letsencrypt on the host.
  • The Nginx container reads those files through a read-only bind mount.
  • A scheduled certbot renew replaces certificates that are close to expiry.
  • A deploy hook runs only after a successful renewal, tests the configuration, and sends HUP to the Nginx master process.

This guide assumes Nginx itself terminates TLS on the instance. If an Application Load Balancer terminates TLS, certificates are managed in that service and this procedure does not apply.

1. Map hostnames to server blocks and certificates

Start with a complete list of every name that must work, such as example.com, www.example.com, and app.example.com. Each name needs two things: a DNS record pointing at the instance, and a server block in Nginx whose server_name lists it. Nginx selects a server block by hostname and, for HTTPS, selects the certificate during the TLS handshake using the requested name (Server Name Indication). A certificate that does not cover the requested name produces a browser or client warning even though the server is up.

One certificate with several names, or separate certificates

Certbot can issue one certificate that lists several names, or separate certificates for each group. The choice mainly affects how far a problem spreads and how easy the renewal set is to reason about.

Consideration One certificate with several names Separate certificate per site or group
Files to mount and reference One path pair (fullchain.pem, privkey.pem) shared by several server blocks One path pair per group, referenced from each server block
Renewal All names renew together; one failed validation can block the set Each lineage renews on its own; one failure is isolated
Changing the name list Requires re-issuing with the full list; adding names needs deliberate expansion Adding a site means issuing a new lineage
Validation ownership Suits domains that share DNS or hosting administration Suits domains managed by different teams or providers

Whichever you choose, keep the name list stable. If you request a certificate for only some of an existing certificate’s names, Certbot may create a separate certificate instead of replacing the original. To add names on purpose, use --expand with the complete list.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Wildcard names

A wildcard such as *.example.com covers one label of subdomains under that name, such as app.example.com. It does not cover example.com itself or deeper names like a.b.example.com, so list the apex name alongside it when both are needed. Certbot supports wildcard issuance only through DNS-01 validation.

Server blocks

The following sketch shows one HTTP block for challenges and redirects, and two HTTPS sites: one served by a shared certificate and one by a separate certificate. Paths and upstream names are placeholders to adapt.

server {
    listen 80;
    server_name example.com www.example.com app.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://app_blue:8080;
    }
}

server {
    listen 443 ssl;
    server_name shop.example.org;

    ssl_certificate     /etc/letsencrypt/live/shop.example.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/shop.example.org/privkey.pem;

    location / {
        proxy_pass http://shop_app:8080;
    }
}

Because the HTTP block sends everything else to HTTPS, every name in that server_name list also needs a matching certificate that Nginx can load.

2. Choose the validation method

ACME validation proves control of each name. The two practical options differ in what they require from the network and the DNS provider.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Decision point HTTP-01 (webroot) DNS-01
What the CA checks A file served at /.well-known/acme-challenge/ on port 80 A TXT record at _acme-challenge under each name
Network requirement Port 80 reachable from the public internet on every validated name Outbound access to the DNS provider’s API; no inbound port needed for validation
Wildcard names Not supported Required
Unattended renewal Works when port 80 and the challenge location stay in place Works only with an automated DNS plugin for your provider; manual DNS challenges cannot renew unattended
Credentials to protect None beyond host access DNS API credentials, which should be scoped to the one zone

HTTP-01 with the webroot method

Use HTTP-01 when every name resolves to the instance and port 80 is open to the internet. The webroot method writes challenge files into a directory that a running web server already serves, so Certbot does not need to stop Nginx. In a container, that directory is a host path that Nginx mounts read-only at the path named in the root directive above.

Create the directory on the host and request the certificate:

sudo mkdir -p /srv/acme-webroot
sudo certbot certonly --webroot -w /srv/acme-webroot 
  -d example.com -d www.example.com -d app.example.com

Certbot writes its challenge files under /srv/acme-webroot/.well-known/acme-challenge/. A failed challenge with a 404 response usually means the location block or the mount is wrong, not that Certbot is at fault.

If any name also has an AAAA record, the validation request may arrive over IPv6. In that case port 80 must answer on the instance’s IPv6 address as well, or validation can fail for reasons that look intermittent.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

DNS-01 for wildcards or for hosts that cannot serve port 80

DNS-01 proves control by creating a TXT record through your DNS provider’s API. It is the only option for wildcard certificates. For unattended renewal, use one of Certbot’s DNS plugins that matches your provider. Certbot’s documentation lists the supported plugins and their setup; confirm that your provider is on the list before designing around it.

If your zone is hosted in Amazon Route 53, the certbot-dns-route53 plugin is one option. Its credentials should come from an IAM policy limited to the hosted zone that holds the records. A typical request looks like this:

sudo certbot certonly --dns-route53 
  -d example.com -d '*.example.com'

Store the credentials in a file readable only by root, and never place them in a Compose file that is committed to source control.

3. Persist certificates and configuration on the host

Files written inside a container are lost when the container is recreated, for example by docker compose up -d after an image change. Keep everything that must survive on the host and mount it into Nginx:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • /etc/letsencrypt: Certbot’s state, renewal configuration, and certificates.
  • /srv/acme-webroot: challenge files for HTTP-01.
  • /srv/nginx/conf.d: the server blocks shown earlier.

A Compose-style example for the Nginx container, using the official image tag and read-only mounts:

docker run -d --name nginx-proxy 
  -p 80:80 -p 443:443 
  -v /srv/nginx/conf.d:/etc/nginx/conf.d:ro 
  -v /srv/acme-webroot:/var/www/certbot:ro 
  -v /etc/letsencrypt:/etc/letsencrypt:ro 
  nginx:stable

Mount the whole /etc/letsencrypt directory, not only live/. Certbot’s live/ entries are symbolic links into archive/. If only live/ is mounted, the links resolve to nothing inside the container, and Nginx fails to start with a missing-certificate error.

Check the resolved paths from inside the container before going further:

docker exec nginx-proxy ls -lL /etc/letsencrypt/live/example.com/
docker exec nginx-proxy nginx -t

The first command should list readable fullchain.pem and privkey.pem files. The second should report that the configuration test is successful.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep the private key tight: the host copy should be readable only by root, and the container should receive it only through the read-only mount, never by copying it into an image.

4. Set the EC2 network rules

Security groups act as instance-level firewalls. For this setup, the relevant rules are:

  • Inbound TCP 80 from 0.0.0.0/0 (and ::/0 if you use IPv6) when using HTTP-01 or redirecting HTTP to HTTPS.
  • Inbound TCP 443 from the clients that should reach the sites.
  • Inbound TCP 22 only from trusted operator address ranges. AWS warns against leaving SSH open to the internet in production.

Also confirm the instance’s route table and subnet settings, any host firewall such as ufw or firewalld, and that each hostname’s A (and AAAA, if present) record points where you expect. A security group that allows port 80 does not help if a host firewall still drops it.

Nginx name-based virtual hosting does not require a separate public IP for each domain. AWS documents multiple addresses as one way to host several sites with several certificates, but it is not a requirement for this design.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

5. Automate renewal and the reload path

Issue once and confirm the renewal settings

After the first successful issuance, Certbot stores a renewal file for each certificate lineage under /etc/letsencrypt/renewal/. That file records the authenticator and the webroot path, so later renewals reuse them. Confirm the saved settings before automating anything:

sudo certbot certificates
sudo cat /etc/letsencrypt/renewal/example.com.conf

Check that the authenticator and webroot path match what you intended and that the domain list is complete.

Schedule the renewal check

Packaged Certbot installs usually include a systemd timer. Confirm that it is active:

systemctl list-timers | grep certbot

If the timer is missing, a cron entry works as well:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
0 */12 * * * root certbot renew --quiet

Running the check twice a day is common practice because certbot renew only replaces certificates that are close to expiry. A renewal attempt that finds nothing to do is normal and produces no reload.

The AWS Lightsail tutorial describes Let’s Encrypt certificates valid for 90 days that can be renewed 30 days before expiry. That page does not state a year, and it describes Lightsail rather than EC2. Treat those numbers as that tutorial’s figures, and confirm the current policy of the certificate authority you use.

Add the deploy hook

Certbot runs executables placed in /etc/letsencrypt/renewal-hooks/deploy/ after each certificate it has renewed successfully. Use that directory rather than a one-off command so the hook survives host changes and applies to every lineage.

  1. Create the hook script:
    sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh <<'EOF'
    #!/bin/sh
    set -eu
    docker exec nginx-proxy nginx -t
    docker kill -s HUP nginx-proxy
    EOF
    sudo chmod 750 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
  2. Run a renewal with the hook in place. Certbot executes the script only if a certificate was actually renewed.
  3. If nginx -t fails, set -e stops the script before the signal is sent, and the running workers keep the configuration they already have.
  4. If the test passes, docker kill -s HUP nginx-proxy sends SIGHUP to the container’s main process, which is the Nginx master, and the master re-reads the configuration.
  5. Check the Certbot log at /var/log/letsencrypt/letsencrypt.log for the hook result.

Two details matter here. If several lineages renew in the same run, the hook runs once per renewed certificate, which is harmless but produces repeated reloads. And the hook needs permission to run docker. Membership in the docker group, or access to the Docker socket, is effectively root access on the host, so run the hook as root and restrict who can use Docker.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How the reload works, and why not restart

NGINX’s runtime control documentation says: “To reload your configuration, you can stop or restart NGINX, or send signals to the master process.” A reload sends the master process a signal to re-read configuration. The master starts new worker processes with the new configuration and asks the old workers to finish the requests they are already handling before they exit. If the new configuration is invalid, the master keeps running the old one, which is why the hook tests first.

Restarting the container or stopping Nginx to pick up new certificates ends the master process and drops in-flight connections, so avoid it for routine renewal. Certbot’s standalone authenticator, which must claim port 80 itself, needs stop and start hooks, and that mode conflicts with the goal here. Use the webroot or DNS methods instead.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

6. Confirm the result from outside the host

A successful hook does not prove that clients receive the new certificate. Check each name from a machine other than the instance:

  • Run certbot renew --dry-run. It exercises the renewal flow against the Let’s Encrypt staging environment and does not save new certificates.
  • Check the presented certificate for each name, changing -servername for each site: openssl s_client -connect app.example.com:443 -servername app.example.com </dev/null | openssl x509 -noout -subject -enddate
  • Confirm the expiry date moved forward after a real renewal, and that the Certbot log shows the hook ran.
  • Record what your monitoring shows during a reload under realistic traffic. Whether any requests failed, and how many, depends on your application, connection lengths, and upstream health, so measure that rather than assuming it.

Troubleshooting

Symptom Likely cause Check
HTTP-01 challenge returns 404 Missing /.well-known/acme-challenge/ location, or the webroot is not mounted at the path in root Run docker exec nginx-proxy ls /var/www/certbot/.well-known/acme-challenge/ while a challenge is running; confirm the mount in docker inspect nginx-proxy
Nginx fails to start after adding the mount Only live/ was mounted, so symlinks into archive/ are broken Mount all of /etc/letsencrypt and run docker exec nginx-proxy ls -lL on the live path
Clients still see the old certificate after renewal Hook did not run, or the reload was not sent Read /var/log/letsencrypt/letsencrypt.log; confirm the script is executable and the container name matches
Renewal creates a new certificate instead of replacing the old one The requested names differ from the existing lineage Compare certbot certificates with the names you requested; use --expand only to add names deliberately
Hook fails at the configuration test A config file references a path or name that no longer exists Run docker exec nginx-proxy nginx -t manually; the old configuration is still serving
Validation fails only for some names A DNS record or IPv6 AAAA record points somewhere port 80 is not answered Resolve each name and test port 80 on every address it returns

Limits of this setup

This design covers one Nginx container on one EC2 instance. It does not cover multiple instances behind a load balancer, where each node needs the same certificate files and a coordinated reload; an Application Load Balancer or another TLS terminator changes where certificates belong. Nginx’s ACME module is a separate architecture, and the NGINX ACME documentation describes module configuration and identifier restrictions, so check those before choosing it instead of Certbot. The AWS Lightsail tutorial is the closest AWS walkthrough for certificate files and domain validation, but it uses manually entered DNS TXT records and restarts services, so it should not be read as a Docker or EC2 zero-downtime procedure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

p>

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.