How to Renew Let’s Encrypt Certificates with Nginx

Renew Let’s Encrypt certificates with Certbot and Nginx, check automatic renewal, reload certificates, and fix common validation failures.

On this page

A certificate renewal should be a background job. If you are logging into your server each time the browser starts complaining, there is usually something missing from the renewal setup.

This post originally covered a short stop-and-start procedure. That needs a correction: you do not normally need to stop Nginx to renew a certificate. The right command depends on how the certificate was issued.

This guide covers an existing Nginx website with certificates managed by Certbot on a Linux server using systemd. It is for a server you manage over SSH. The commands below are examples to adapt to your server, not a record of a live renewal tested for this update.

TL;DR

  • Find the certificate name and saved renewal method first.
  • Test with sudo certbot renew --dry-run, then use sudo certbot renew for certificates due for renewal.
  • Keep Nginx running when using the Nginx or webroot method. Standalone validation may need port 80 freed.
  • Check the renewal scheduler and make sure Nginx loads the renewed certificate.
  • Verify the certificate visitors actually receive, especially if there is a proxy being used in front of the server.

Check what is already installed

Connect to the server over SSH. Run these commands in its terminal:

Terminal window
certbot --version
sudo certbot certificates
sudo nginx -t

The certificate listing gives you the certificate name, covered domains, expiry date and file paths. Keep the name handy. It might be example.com-0001, even though the website is example.com.

Use your actual certificate name wherever this guide uses example.com. If Certbot lists nothing, stop here: the certificate might belong to another client, container or hosting service. Installing a second client will not fix the original renewal job.

If certbot is missing, check how the server was set up. Older installations may call it letsencrypt. For installation or upgrades, follow the Certbot instructions for your operating system and web server (opens in a new tab). Keep one installation method so the scheduled job runs the same client you use manually.

nginx -t should report a successful configuration test. Fix any errors before asking Certbot to change the server configuration.

Before editing files, make a root-only backup of /etc/nginx and /etc/letsencrypt using your normal server backup process. The latter contains private keys. Keep that backup out of public folders and Git.

Find the renewal method

Read the matching renewal file, replacing the filename with your certificate name:

Terminal window
sudo less /etc/letsencrypt/renewal/example.com.conf

Look under [renewalparams] for authenticator, installer and any hooks. Avoid editing this file manually.

Saved authenticatorWhat handles validationDoes Nginx stay running?
nginxCertbot’s Nginx pluginYes
webrootA challenge file served by your existing siteYes
standaloneCertbot’s temporary HTTP serverIt needs a free listening port, normally port 80
A DNS pluginA DNS TXT recordYes

Certbot remembers the saved options for renewal. Its user guide (opens in a new tab) covers renewal, hooks, scheduling and configuration changes.

Repeating certonly -d example.com is not the usual renewal procedure.

Test and apply renewal

For an existing Nginx or webroot setup, leave Nginx running and test one certificate:

Terminal window
sudo certbot renew --cert-name example.com --dry-run

A dry run uses the staging service without replacing your live certificate. It can still run pre/post hooks and temporarily reload the web server. Read the saved hooks first, especially on an old standalone setup.

A successful result should report that the simulated renewal succeeded. If it fails, fix the reported problem before proceeding.

Then request a normal renewal:

Terminal window
sudo certbot renew --cert-name example.com

Certbot can report that renewal is not due yet. That is fine. Avoid adding --force-renewal just to make it do something. Repeated live requests can hit issuance limits.

To check every certificate managed by this installation, omit --cert-name:

Terminal window
sudo certbot renew --dry-run
sudo certbot renew

Do not build a calendar reminder around a fixed certificate lifetime. Check the actual expiry and let the client decide when renewal is due.

Make sure Nginx uses the renewed files

Renewing the files and serving them to visitors are separate checks.

Inspect the HTTPS server block for your site. With the default Certbot storage layout, these directives should point to the paths reported by certbot certificates:

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

These two lines belong inside your existing HTTPS server block. They are not a complete Nginx configuration. Keep its other settings, including the application or reverse-proxy configuration.

Use the live paths rather than copying numbered files from archive. Do not loosen private-key permissions to get past an error.

If you manage certificate installation yourself, test and reload after a successful renewal:

Terminal window
sudo nginx -t && sudo systemctl reload nginx

A Nginx reload applies configuration gracefully while existing requests finish. If the test fails, nginx -t reports the configuration file and line number to fix. In this context, /etc/nginx/sites-enabled/example.com:42 means line 42 in that site’s configuration file. Your server may use a file under /etc/nginx/conf.d/ or /etc/nginx/nginx.conf instead. Fix the file shown in the error, or restore it from your backup, then run sudo nginx -t again before reloading.

For unattended renewal with webroot or another certonly setup, add a deploy hook if no existing hook handles the reload. First check /etc/letsencrypt/renewal-hooks/deploy/ and the saved renewal options to avoid duplicating one.

Create /etc/letsencrypt/renewal-hooks/deploy/reload-nginx with this content:

#!/bin/sh
set -eu
nginx -t
systemctl reload nginx

Make it executable:

Terminal window
sudo chmod 750 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx

This directory hook applies to successful renewals managed by this Certbot installation, so use it only where reloading this Nginx service is appropriate. A setup using the Nginx installer may already handle deployment.

Test applicable deploy hooks explicitly:

Terminal window
sudo certbot renew --cert-name example.com --dry-run --run-deploy-hooks

This reloads using the current live certificate after a successful simulation. To undo this added hook, remove only the file you created and restore your previous reload arrangement.

Check automatic renewal

Look for a scheduled systemd timer:

Terminal window
systemctl list-timers --all

Find the Certbot renewal entry and check its next run. Unit names vary between installation methods. If yours is certbot.timer, inspect it and the matching service logs with:

Terminal window
systemctl status certbot.timer
sudo journalctl -u certbot.service --since '7 days ago'

Use the names actually listed on your server. Some installations use cron instead. Check /etc/crontab and /etc/cron.d/ if no timer appears.

If there is no scheduled job, follow the automated renewal instructions in the Certbot user guide linked above for your installation. A timer waiting for its next run is useful, but a successful dry run tells you more about whether validation still works. Use an external monitoring service to check your website’s certificate expiry and alert you before it expires. If the renewal job fails in the background, the alert gives you time to fix it before visitors see a certificate warning.

When renewal fails

Start with Certbot’s error and /var/log/letsencrypt/letsencrypt.log. Avoid retrying live issuance in a loop.

Timeout, connection refused or the wrong challenge response

For HTTP validation, Let’s Encrypt must reach the challenge through public port 80. Check:

  • The domain’s A record (IPv4) points to your web server. If it also has an AAAA record (IPv6), check that address too. An old address can send Let’s Encrypt to the wrong server.
  • The host firewall, cloud firewall and router allow incoming port 80.
  • A reverse proxy, CDN, login screen or application rewrite is not swallowing /.well-known/acme-challenge/.
  • Every frontend that can receive the request serves the challenge correctly.

With webroot, the saved webroot directory must match the directory Nginx actually serves for that path. With the Nginx plugin, check the domain’s server_name and installed plugin using sudo certbot plugins.

Wildcard certificates need DNS validation. DNS validation also helps where incoming port 80 is unavailable, but unattended renewal needs automated DNS updates and carefully scoped API credentials.

Standalone cannot bind to port 80

Check which process uses the port 80:

Terminal window
sudo ss -ltnp 'sport = :80'

If Nginx owns it and the saved authenticator is standalone, the old stop-and-start approach explains the conflict. Plan a maintenance window before stopping it. Bring Nginx back up even if renewal fails, and check systemctl status nginx afterwards.

For a regular website, consider moving to the Nginx plugin so future renewals can happen with the site running. With Certbot 2.3.0 or newer and the Nginx plugin installed, test and save that change using:

Terminal window
sudo certbot reconfigure --cert-name example.com --authenticator nginx --installer nginx

Review old stop/start hooks as part of the migration. Do not assume changing the plugin removes them. The Certbot user guide linked above covers version differences. Run a fresh dry run after reviewing the saved settings.

Renewal succeeded, but the browser still shows an expired certificate

Check the public endpoint from another machine with OpenSSL installed:

Terminal window
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates

Look at notAfter and compare it with the certificate listed by Certbot. This command inspects the presented certificate. It is not a complete trust or hostname verification. Also open the HTTPS site in a browser and check for certificate errors.

If the dates differ, check the Nginx certificate paths and reload result. Then check whether traffic reaches a different server. A CDN or load balancer may serve its own certificate, so the public check can show that certificate rather than the one on your origin.

The useful end state is simple. The site serves the expected certificate, a renewal simulation succeeds, and the scheduled job can repeat it without someone remembering to SSH in.

← All posts

Type to search the published collection.