# Automate SSL Certificate Renewal with Certbot and NGINX

> Set up automatic HTTPS with Certbot and Let's Encrypt on Ubuntu, configure NGINX as a TLS terminator, and never manually renew an SSL certificate again.

URL: https://www.matthewswong.com/en/blog/automate-ssl-certbot-nginx/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2025-06-10
- Updated: 2026-10-17
- Category: DevOps

Manually managing SSL certificates is one of those tasks that feels simple until a certificate expires in production at 2 AM on a Sunday. Let's Encrypt and Certbot automate the entire lifecycle — issuance, validation, installation, and renewal — so that SSL certificate expiry simply stops being a concern. Once this is set up on every DigitalOcean Droplet or GCP Compute Engine instance you manage, there is no certificate to renew by hand. This guide covers the complete setup, the NGINX configuration for strong TLS, and the automated renewal pipeline.

## How Let's Encrypt and Certbot Work

Let's Encrypt is a free Certificate Authority (CA) that issues Domain Validation (DV) certificates valid for 90 days. The short validity window is intentional — it enforces automation and limits the damage window for compromised certificates. Certbot is the official ACME client that communicates with Let's Encrypt's API. ACME (Automatic Certificate Management Environment) is the protocol that proves you control a domain before the CA issues a certificate. Certbot supports two challenge types: HTTP-01 (creates a temporary file at /.well-known/acme-challenge/ on your web server) and DNS-01 (creates a temporary DNS TXT record). HTTP-01 is simpler and works for most setups.

### The 90-Day Certificate Lifecycle

A Let's Encrypt certificate lasts 90 days. Certbot's recommended renewal approach is to run `certbot renew` twice daily (via systemd timer or cron). The command checks whether any certificate is within 30 days of expiry — if not, it does nothing. If a renewal is needed, it re-runs the ACME challenge, obtains a new certificate, and runs any configured deploy hooks (such as reloading NGINX). This approach means certificates are typically renewed when they have 30 days remaining, giving you a 30-day buffer even if the renewal process fails for a few days.

### Rate Limits to Know

Let's Encrypt enforces rate limits to prevent abuse. The most relevant for developers: 50 certificates per registered domain per week, and 5 failed validation attempts per hour per account per hostname. These limits matter during initial setup when you may be iterating on your configuration. Always use `--dry-run` flag when testing your certbot commands — dry runs use the Let's Encrypt staging environment (separate rate limits) and do not issue real certificates. Only remove `--dry-run` when you are confident the configuration is correct.

> If your server is behind a cloud firewall or DigitalOcean's Droplet firewall, ensure port 80 (HTTP) is open during the ACME HTTP-01 challenge. Certbot's NGINX plugin temporarily serves the challenge file on port 80. After the challenge completes successfully, you can restrict port 80 to only redirect to HTTPS. Certbot will continue to handle renewals automatically because the NGINX plugin manages the temporary port-80 exposure during the renewal process.

## Installation and Initial Certificate

The NGINX plugin for Certbot is the recommended approach — it automatically edits your NGINX configuration to add the certificate paths, enable HTTPS, and add the HTTP-to-HTTPS redirect. After running `certbot --nginx`, your NGINX configuration will have `ssl_certificate` and `ssl_certificate_key` directives pointing to `/etc/letsencrypt/live/'<'domain>/`. The plugin also adds `include /etc/letsencrypt/options-ssl-nginx.conf` which provides a secure default cipher suite and protocol configuration maintained by Let's Encrypt.

### Deploy Hooks for NGINX Reload

After Certbot renews a certificate, NGINX needs to reload to pick up the new certificate. Without a deploy hook, the old certificate stays loaded in memory until NGINX is restarted manually or the server reboots. Certbot runs scripts in `/etc/letsencrypt/renewal-hooks/deploy/` after every successful renewal. Create a script that runs `nginx -t && systemctl reload nginx` — the config test ensures a broken NGINX configuration does not take down your server during an automated renewal.

> **Port 80 Must Be Open During Renewal**

> The most common mistake when setting up Certbot is running the initial `certbot certonly` or `certbot --nginx` command when port 80 is blocked. The HTTP-01 challenge fails silently from the user's perspective — you get a rate-limit hit and have to wait an hour before retrying. Check that `curl http://your-domain.com/.well-known/acme-challenge/test` returns something (even a 404 from NGINX) before running Certbot. Also, never delete `/etc/letsencrypt/` — this directory contains your account key and certificate renewal configuration. Back it up to DigitalOcean Spaces or GCP Cloud Storage.

## Hardening TLS Configuration

The default Certbot NGINX configuration is secure but not optimal for an A+ rating on SSL Labs. To achieve maximum security score: disable TLS 1.0 and 1.1 (keep only TLSv1.2 and TLSv1.3), configure a strong cipher suite prioritizing ECDHE with forward secrecy, enable HSTS with a 2-year max-age and includeSubDomains, add OCSP stapling to reduce latency on TLS handshakes, and configure a Diffie-Hellman parameter file (2048-bit minimum, 4096-bit preferred). Generate the DH params file with `openssl dhparam -out /etc/nginx/dhparam.pem 2048` — this takes 1-2 minutes on a typical VPS.

## Wildcard Certificates with DNS-01 Challenge

For environments with multiple subdomains (api.example.com, app.example.com, admin.example.com), a wildcard certificate (`*.example.com`) is more efficient than individual certificates per subdomain. Wildcard certificates require the DNS-01 challenge, which proves domain ownership by creating a DNS TXT record rather than serving a file over HTTP. For DigitalOcean DNS, install the `python3-certbot-dns-digitalocean` plugin and create a credentials file with your DigitalOcean API token. The challenge and renewal are then fully automated without any HTTP access needed.

## Monitoring Certificate Expiry

Even with automated renewal, you should monitor certificate expiry as a safety net. Add a Prometheus Blackbox Exporter probe that checks TLS certificate expiry for each domain and alert when expiry is less than 21 days away. Alternatively, Let's Encrypt sends expiry reminder emails to the address registered with Certbot. For a belt-and-suspenders approach, use a service like certificate.transparency.dev or an uptime monitor with TLS expiry checking (UptimeRobot's free tier includes this feature) as a secondary notification channel outside your monitoring stack.

## Sources & Further Reading

- EFF — Certbot Official Documentation — https://certbot.eff.org/docs/
- Let's Encrypt — FAQ and Documentation — https://letsencrypt.org/docs/faq/
- NGINX — Configuring HTTPS Servers — https://nginx.org/en/docs/http/configuring_https_servers.html

## Source links

- https://certbot.eff.org/docs/
- https://letsencrypt.org/docs/faq/
- https://nginx.org/en/docs/http/configuring_https_servers.html

## FAQ

### Why does Let's Encrypt issue certificates that are only valid for 90 days instead of a year or more?

The short 90-day validity window is intentional. It enforces automation by making manual renewal impractical, and it limits the damage window if a private key is ever compromised. The expectation is that Certbot handles renewal automatically, so the short lifespan is a feature, not a limitation.

### How does Certbot avoid hitting Let's Encrypt rate limits during setup?

Always use the --dry-run flag when testing your Certbot commands. Dry runs use Let's Encrypt's staging environment, which has separate rate limits, and do not issue real certificates. Only remove --dry-run once you are confident the configuration is correct. The most relevant production limits are 50 certificates per registered domain per week and 5 failed validation attempts per hour per hostname.

### Why do I need a deploy hook, and what should it run?

After Certbot renews a certificate, NGINX continues serving the old certificate from memory until it is reloaded. Without a deploy hook, the renewed certificate is written to disk but never picked up. Place a script in /etc/letsencrypt/renewal-hooks/deploy/ that runs nginx -t && systemctl reload nginx — the config test prevents a broken NGINX configuration from taking the server down during an automated renewal.

### When should I use a wildcard certificate instead of individual per-subdomain certificates?

A wildcard certificate (*.example.com) is more efficient when you have multiple subdomains such as api, app, and admin under the same domain. However, wildcard certificates require the DNS-01 challenge instead of the simpler HTTP-01 challenge. For DigitalOcean DNS, install the python3-certbot-dns-digitalocean plugin and provide an API token credentials file — renewal then runs fully automatically without any HTTP access needed.

### What monitoring should I keep in place even after automating renewal?

Automated renewal is not a reason to stop monitoring certificate expiry. The post recommends adding a Prometheus Blackbox Exporter probe that alerts when expiry is less than 21 days away. As a secondary channel outside your monitoring stack, an uptime monitor with TLS expiry checking — such as UptimeRobot's free tier — provides a belt-and-suspenders safety net alongside Let's Encrypt's own expiry reminder emails.

---

- HTML version: https://www.matthewswong.com/en/blog/automate-ssl-certbot-nginx/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/automate-ssl-certbot-nginx/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
