Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Linux Admins By James Joyner IV · · 10 min read

Managing TLS Certificates with Certbot and Let's Encrypt

Issue, renew, and debug Let's Encrypt certificates with Certbot on Linux. Handle DNS challenges, automate renewals, and use AI to decode openssl and ACME errors.

  • #linux
  • #tls
  • #certbot
  • #security
  • #sysadmin

Nothing erodes trust faster than an expired TLS certificate. The browser throws a full-page scary warning, customers assume you’ve been hacked, and you find out from Twitter instead of monitoring. The maddening part is that it’s almost always preventable — Let’s Encrypt and Certbot made free, automated certs a solved problem years ago, and yet expired certs still take sites down weekly because someone’s cron job silently broke.

I lean on AI heavily for the part of TLS that’s genuinely hard to keep in your head: the openssl incantations and the cryptic ACME error strings. The model is a fast junior engineer that remembers the exact openssl x509 flags I always forget. What it doesn’t do is touch my servers or my DNS — issuing and deploying a cert is a production mutation, and that stays in human hands with credentials the model never sees.

Pick the right challenge type

Certbot proves you control a domain via an ACME challenge, and the type you pick shapes everything else:

  • HTTP-01 — Certbot drops a file under /.well-known/acme-challenge/ and Let’s Encrypt fetches it over port 80. Simplest, but no wildcards and you need inbound 80.
  • DNS-01 — you publish a TXT record. Required for wildcard certs (*.example.com) and works behind firewalls, but needs DNS API access.

For a standard web server, the webroot or nginx plugin handles HTTP-01 cleanly:

sudo certbot --nginx -d example.com -d www.example.com

For a wildcard, you’re doing DNS-01:

sudo certbot certonly --manual --preferred-challenges dns -d '*.example.com'

If you’re unsure which fits your setup, that’s a good question for an assistant. I keep certificate prompts with my other linux admin prompts:

I run nginx behind a load balancer that terminates port 80, and I need a wildcard cert for *.example.com. Which Certbot challenge type should I use and why, and what DNS records will I need to create?

Verify before you trust the cert

Once issued, confirm the cert is actually what you think it is. The openssl commands I always reach for:

# What's in the file on disk
sudo openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -dates -subject -issuer

# What the server is actually serving (catches deploy mistakes)
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

That second command is the one that matters — it checks what the running server presents, not what’s on disk, which catches the classic “renewed the cert but never reloaded nginx” mistake. Paste the output into the AI when something looks off: “This s_client output shows the old expiry but the file on disk is renewed — what’s the likely cause?” It’ll point you at the missing reload almost every time. The code review helper is also handy for reviewing a deploy hook script before you wire it in.

Automate renewal — and prove it works

Certbot installs a systemd timer or cron job to renew automatically. The failure mode isn’t renewal itself; it’s that the renewed cert never gets loaded by the service. Always set a deploy hook:

sudo certbot renew --dry-run

The --dry-run exercises the whole renewal against the staging server without burning rate limits or changing your real cert. If that passes, your renewals work. Wire the reload into the renewal config:

# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/sh
systemctl reload nginx

Pro Tip: The number one cause of expired certs in 2026 isn’t failed renewal — it’s a successful renewal with no reload hook, so the service keeps serving the cached old cert until it expires. Always confirm there’s a deploy hook and that certbot renew --dry-run exits clean.

Decode ACME and rate-limit errors with AI

ACME errors are dense and the docs are scattered. This is where the model shines as a translator. Common ones:

  • urn:ietf:params:acme:error:unauthorized — the challenge file or TXT record wasn’t reachable. Usually a webroot path mismatch or DNS propagation delay.
  • too many certificates already issued — you hit the rate limit, almost always from a loop of failed-then-retried issuance. Use staging until it works.
  • DNS problem: NXDOMAIN — the validation record isn’t visible to Let’s Encrypt’s resolvers yet.

Paste the full error and ask: “Certbot failed with this ACME error during a DNS-01 challenge. What’s the likely cause and how do I confirm it?” The AI will usually tell you to check propagation with dig:

dig +short TXT _acme-challenge.example.com @8.8.8.8

It reads the error and suggests the diagnostic. You run the dig, you decide whether to retry. The incident response helper covers the same ground when a cert failure is taking a service down and you want a structured investigation path.

Don’t hand the AI your DNS keys

Here’s the hard line for TLS work specifically: DNS-01 needs API credentials for your DNS provider, and those credentials can rewrite your entire zone. They never go near a chat window, never get pasted into a prompt “for context,” and never get embedded in something the model can execute. The same goes for the private key in /etc/letsencrypt/live/*/privkey.pem — that file is the whole point of the cert, and it stays on the box. The AI helps you read the public cert, decode the error text, and draft the renewal script — all things that contain no secrets.

This is the general rule applied to its sharpest case: AI is great at first drafts and pattern-matching, but anything that mutates production — issuing a cert, editing a DNS zone, reloading a live web server — keeps a human in the loop with credentials the model never touches.

Monitor expiry independently

Even with perfect automation, monitor expiry from the outside as a backstop:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -enddate

Run that on a schedule and alert if the expiry is under, say, 14 days. The monitoring alerts helper can draft that alert rule for you. I keep my certbot deploy hooks and the monitoring snippet in the prompt packs and prompts library so spinning up TLS on a new host is a five-minute checklist.

Conclusion

TLS certificate management is a solved problem that still breaks sites, almost always because of a missing reload hook or a silently failed renewal. Get the challenge type right, verify what the server actually serves with s_client, wire a deploy hook, and prove it with --dry-run. Let AI decode the gnarly ACME and openssl output — it’s fast and accurate there — while you keep the DNS keys, the private key, and the act of touching production firmly in human hands.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.