All articles
169 articles · updated weekly See our Tools
All articles
Tutorials

Let's Encrypt: how renewal works

Let's Encrypt: how renewal works, the problems it solves, and how to avoid expired certificates taking down your production site.

Let's Encrypt: how renewal works
COVER · Tutorials

The certificate expired at 3 a.m. on a Sunday

The first time this caught me, monitoring screamed at three in the morning: site down, browsers spitting NET::ERR_CERT_DATE_INVALID, customers unable to pay. The certificate had expired. certbot was installed, the timer running, everything "automatic" — and it expired anyway. The problem wasn't the renewal, it was that the server never reloaded the new certificate. The renewal had worked silently and nobody applied the result.

This piece explains how Let's Encrypt renewal actually works, where it tends to fail, and how not to get woken at 3 a.m. by an expired certificate. First, if you still doubt a free certificate in production, read whether a free SSL certificate is secure — spoiler: it's the same TLS as any other.

Why certificates last only 90 days

If you come from a one-year paid certificate, Let's Encrypt's short validity feels strange: ninety days. It isn't a technical limitation, it's a design decision, and a good one.

Short validity forces automation. If a certificate lasts a year, you renew it manually, forget, and it expires — exactly the scenario in the opening paragraph. At ninety days, manual is unsustainable, so you automate, and automation is the correct state. The second reason is to limit the damage of a leaked key: if a private key leaks, the exposure window is at most ninety days, not a full year. Short validity is a security property, not an annoyance.

ACME and the challenge: HTTP-01 vs DNS-01

Behind all of it is the ACME protocol. To issue or renew, the CA needs proof that you control the domain. That happens via a challenge, and the two main ones are:

  • HTTP-01. The CA asks you to serve a specific file at http://your-domain/.well-known/acme-challenge/<token>. If it can download it, you control the domain on port 80. Simple, but it needs port 80 open and a web server answering.
  • DNS-01. You publish a TXT record at _acme-challenge.your-domain. The CA queries DNS and validates. More work, because it needs programmatic access to your DNS provider, but it's the only one that works for a wildcard certificate.
# HTTP-01, the nginx plugin handles the challenge and the reload
sudo certbot --nginx -d example.com -d www.example.com

# DNS-01, required for wildcard
sudo certbot certonly --dns-cloudflare -d "*.example.com" -d example.com

The rule is blunt: if you want *.example.com, forget HTTP-01. Wildcard only comes out with DNS-01.

How automatic renewal happens

Modern certbot installs a systemd timer (or a cron entry) that runs certbot renew twice a day. You can check it:

# the timer that triggers renewal
systemctl list-timers | grep certbot

# simulates renewal without applying anything
sudo certbot renew --dry-run

Here lives a detail many people don't know: certbot renew doesn't renew every day. It only acts when the certificate is within thirty days of expiry — that is, around day sixty of ninety. Before that, the command runs, checks, and does nothing. That's deliberate: it gives a thirty-day cushion for something to go wrong with time to fix it before expiry. The --dry-run exercises the whole flow against the CA's staging environment without spending a real issuance.

The classic failure: renewed but didn't reload

Back to the Sunday-night certificate. The renewal worked: certbot downloaded a new, valid certificate, saved to disk. But nginx (or Apache, or whatever) loads the certificate into memory at startup and doesn't re-read the file on its own. Result: new file on disk, old certificate in memory, and the old one expires.

The fix is the deploy-hook — a command certbot runs only when a renewal actually happened:

sudo certbot renew --deploy-hook "systemctl reload nginx"

The --nginx and --apache plugins already reload for you; the hole shows up precisely for those using certonly and managing the server by hand. If you renew with certonly, the deploy-hook isn't optional. Without it, you fall straight into the trap of a green renewal in the log and a site down in production.

Monitor expiry so you're not surprised

Blindly trusting automation is like the guy who never tests his backup. The automation will fail one day — the DNS token expires, port 80 gets blocked by a new firewall rule, an upgrade breaks the plugin. What separates a scare from an incident is finding out before expiry.

# how many days until the certificate expires
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
  | openssl x509 -noout -enddate

Put in an external check that alerts when, say, twenty days remain. External is the keyword: it must see the certificate from the outside, from a visitor's point of view, not trust the local log that says "renewed successfully." The log lies when the reload fails. The external check doesn't.

Frequently asked questions

Do I ever need to renew the certificate manually?

Under normal conditions, no — the certbot timer handles it. You renew manually only to resolve a failure, test a config change, or force issuance after fixing something. If you're renewing by hand routinely, the automation is broken and worth investigating.

Why does renewal run at day 60 and not day 89?

Because the thirty-day window is a safety margin. If renewal fails for any reason at day sixty, there are still nearly thirty days of daily attempts before the certificate expires. Renewing only on the last day would leave no room to fix anything.

What changes when renewing a wildcard certificate?

Wildcard requires the DNS-01 challenge, so the automation needs your DNS provider's credentials to create and remove the TXT record at each renewal. You can't renew a wildcard with HTTP-01. The rest of the certbot flow is the same.

Does renewal produce an identical certificate or a new one?

It produces a new certificate, with new validity and, depending on configuration, a new key. That's why the server reload is mandatory: the file on disk actually changes. Treating renewal as "just extending what exists" is the root of the failure to reload.

What to secure before the next expiry

Let's Encrypt renewal is reliable once you understand the three parts: ACME proves the domain via a challenge (HTTP-01, or DNS-01 for wildcard), the certbot timer renews around day sixty, and — the point that brings most sites down — the server must reload the new certificate via deploy-hook. Automate, but don't trust blindly: run --dry-run, ensure the reload, and put in an external expiry monitor. The ninety-day validity is a security ally, as long as the automation is genuinely closing the loop.

RD
Author
Rafael Duarte
Backend developer with a background in fintech and B2B SaaS — worked on teams that scaled APIs from zero to millions of requests. Carries enough production scars to hold strong opinions on tools, patterns and architecture decisions. Not an academic: he read the UUID RFC when he had to choose between v4 and v7 for a write-heavy table.
View profile