Losing a domain because it silently expired is one of the most avoidable disasters in web infrastructure. Your site goes dark, your email stops working, and in the worst case a squatter registers it before you can renew. It happens more often than you'd expect — especially when auto-renew fails silently, when domains are managed across multiple registrars, or when the original billing email no longer works.
This guide covers three ways to monitor domain expiry and get alerted before it's too late.
| Method | Setup time | Maintenance | Best for |
|---|---|---|---|
| WHOIS cron script | 20 min | Low | Linux servers, technical teams |
| Registrar auto-renew + calendar | 5 min | None | Personal sites, single registrar |
| Dedicated monitoring service | 2 min | None | Multiple domains, always-on alerting |
The most direct approach: query WHOIS data for your domain and parse the expiry date. When days remaining drops below a threshold, send an alert.
First, install whois if it's not already available:
sudo apt-get install -y whois # Debian/Ubuntu
sudo yum install -y whois # CentOS/RHEL
Create the monitoring script at /usr/local/bin/check-domain-expiry.sh:
#!/bin/bash
# Domain expiry checker — alerts when expiry is within N days
DOMAIN="$1"
ALERT_DAYS="${2:-30}" # Alert if expiry is within 30 days
ALERT_EMAIL="ops@example.com"
if [ -z "$DOMAIN" ]; then
echo "Usage: $0 <domain> [days-before-alert]"
exit 1
fi
# Query WHOIS and extract expiry date
EXPIRY_RAW=$(whois "$DOMAIN" 2>/dev/null | grep -iE "expir(y|ation).*date|paid-till" | head -1)
EXPIRY_DATE=$(echo "$EXPIRY_RAW" | grep -oE "[0-9]{4}-[0-9]{2}-[0-9]{2}|[0-9]{2}/[0-9]{2}/[0-9]{4}" | head -1)
if [ -z "$EXPIRY_DATE" ]; then
echo "ERROR: Could not parse expiry date for $DOMAIN"
echo "WHOIS output: $EXPIRY_RAW"
exit 1
fi
# Convert to epoch (normalize date format)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s 2>/dev/null || date -j -f "%Y-%m-%d" "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
echo "Domain: $DOMAIN | Expires: $EXPIRY_DATE | Days left: $DAYS_LEFT"
if [ "$DAYS_LEFT" -le "$ALERT_DAYS" ]; then
echo "ALERT: $DOMAIN expires in $DAYS_LEFT days ($EXPIRY_DATE)" | \
mail -s "⚠️ Domain expiry warning: $DOMAIN" "$ALERT_EMAIL"
echo "Alert sent to $ALERT_EMAIL"
fi
Make it executable and test it:
chmod +x /usr/local/bin/check-domain-expiry.sh
/usr/local/bin/check-domain-expiry.sh github.com 365
Add a cron job to run daily for each domain you manage:
# Check domain expiry daily at 9am
0 9 * * * /usr/local/bin/check-domain-expiry.sh yourdomain.com 30
0 9 * * * /usr/local/bin/check-domain-expiry.sh yourdomain.io 30
mail command requires a working mail transfer agent on the server.RDAP (Registration Data Access Protocol) is the modern replacement for WHOIS. It returns structured JSON, which is far easier to parse reliably:
curl -s "https://rdap.org/domain/github.com" | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
events = data.get('events', [])
for e in events:
if e.get('eventAction') == 'expiration':
print('Expires:', e.get('eventDate', 'unknown'))
"
For personal sites and small portfolios, a belt-and-suspenders approach with auto-renew and explicit calendar reminders works well — as long as you actually do it.
The steps:
A dedicated monitoring service checks your domains regularly and emails you before they expire — from outside your infrastructure, so it works even if your own servers are down.
The key advantage of an external service over a cron script on your server: it doesn't share failure modes with your infrastructure. If the server running your cron script has a disk failure or gets rebooted, the domain monitoring stops silently. An external service continues watching regardless.
What good domain expiry monitoring looks like:
While you're setting up domain expiry monitoring, it's worth thinking about the other expiry signals that cause silent outages:
| What expires | Typical failure mode | Alert threshold |
|---|---|---|
| Domain name | Site goes down, email stops, squatting risk | 30 days |
| SSL certificate | "Not secure" warning in browsers, HTTPS broken | 30 days |
| API keys / tokens | Silent 401s in production | 30 days |
| TLS client certs | Service-to-service auth failures | 30 days |
| Code signing certs | App distribution failures, rejected builds | 60 days |
SSL certificate expiry is the most common silent failure we see — and unlike domain expiry, it doesn't get email reminders from registrars. Certificates issued by Let's Encrypt auto-renew (when certbot is running), but certificates from other CAs don't, and Let's Encrypt auto-renewal fails silently when the certbot cron job stops working.
Use a cron script if you already have reliable server infrastructure and want zero external dependencies. Budget 20–30 minutes to write it correctly with proper error handling and email delivery.
Use registrar auto-renew + calendar reminders if you manage fewer than five domains and they all live at the same registrar. It's not automated monitoring, but it's a workable safety net if you actually follow through on the calendar reminders.
Use a dedicated monitoring service if you manage multiple domains across different registrars, if you want external verification that works even when your infrastructure has problems, or if you want SSL certificate monitoring bundled with domain monitoring in the same dashboard.
Domain expiry and SSL certificate expiry are the two most common silent failures that take sites offline without warning. If you're setting up domain monitoring, SSL monitoring is the natural next step — and it doesn't require registrar access.
Enter any domain to check its SSL certificate expiry instantly:
Check SSL expiry free → Get SSL expiry alerts →