There are two failure states for SSL expiry alerts.
The first is obvious: no alerts at all. The certificate expires, the site breaks, someone notices.
The second is subtler: alerts that exist but arrive too late. A single notification at seven days leaves almost no buffer if the renewal hits a snag. A single notification at 30 days is ignored because it feels distant. Neither is a real safety net.
Here's how to set alerts up correctly — including the parts that a local script can't check.
Why Auto-Renewal Isn't the Same as Monitoring
Auto-renewal handles the happy path. When everything is working — the certbot timer is active, port 80 is reachable, the DNS is clean, the server reloads after renewal — certificates rotate without anyone noticing.
The problem is that each of those dependencies can break silently:
- ✓The systemd timer gets dropped during a server migration
- ✓A firewall rule added after the original setup blocks the ACME challenge
- ✓The certificate renews but the server never reloads, so the old cert keeps being served
- ✓A Wildcard cert's DNS API credentials expire
None of these produce an immediate error. The certificate keeps working until it expires. Auto-renewal failing and auto-renewal succeeding look identical from the outside until the expiry date arrives.
Alerts exist to catch the gap between "something went wrong" and "the site broke."
What a Useful Alert Window Looks Like
A single alert is not enough. A sequence gives you time to act at whatever point the problem is caught:
- ✓30 days — the initial signal. Plenty of time to investigate a renewal issue, rotate a manually managed cert, or update a wildcard.
- ✓14 days — confirmation the 30-day alert wasn't acted on, or that a new problem appeared.
- ✓7 days — escalation. Any unresolved issue at this point is urgent.
- ✓1 day — last call. If you're seeing this, something has gone wrong and needs immediate attention.
The window matters more than the alert itself. A single notification at any one of these points is gambling on response time.
The DIY Script Approach
It's possible to script an SSL expiry check with standard tools:
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null \
| openssl x509 -noout -enddateWrap that in a cron job, compare the output date to today, and send an email if it's within your threshold.
This works, but it has two gaps worth knowing about.
First, it only checks what's on disk, not necessarily what the server is serving. If a certificate renews but the server doesn't reload, the script reads the new file and reports everything is fine — while the server continues to serve the expired cert to every visitor.
Second, it requires maintenance. The script is running on the same server it's monitoring. A server rebuild, a cron misconfiguration, or a VM migration silently stops the checks.
External Monitoring Checks What Visitors Actually See
External monitoring connects to the domain the same way a browser does — a live TLS handshake — and reads the expiry date from the certificate that's actually being served. Not from disk. Not from a config file. From the handshake.
This is the check that catches the renewed-but-not-reloaded case, because it sees what's being served to the public, not what the server thinks it's serving.
No agent, no credentials, no access to the server required. The check is network-only, which means it also survives server migrations, rebuilds, and cron misconfiguration — because it's running from outside.
Domain Expiry Needs Its Own Alert
SSL expiry alerts don't cover domain registration expiry. The two systems are completely separate.
A WHOIS query returns the domain registration expiry date from the registrar's public records. An SSL certificate check doesn't touch this. If the domain registration lapses, DNS resolution fails and the SSL certificate becomes irrelevant — there's no hostname for it to be attached to.
Both need dedicated monitoring with the same alert window logic. A setup that covers SSL but not domain registrations has a blind spot that produces the same outage.
Setting It Up Without Installing Anything
ExpiryPing handles both checks externally — SSL via TLS handshake, domain via WHOIS — with alerts at 30, 14, 7, and 1 day. Email and Slack. No agent, no server access, no credentials stored.
You add the hostname, it watches. Setup is under five minutes. Free for up to 3 domains, paid plans from $19/month for up to 10.
The point isn't to replace certbot or auto-renewal — keep those running. The point is to have an independent check that tells you when the automatic process fails, before visitors find out instead.