Certificate Expiry Monitoring — A Practical Guide
Certificates expire. It is not a question of if — only when. An expired certificate causes immediate, complete service outages. This guide covers how to monitor certificate expiry proactively so you never get caught out.
Why certificates expire in production
You might wonder — if engineers know certificates expire, why do they still expire in production? The answer is always the same: the person who installed the certificate is not the same person responsible for renewing it, or the renewal process was manual and got missed during a busy release cycle.
Alert thresholds that actually work
A single "expires in 30 days" alert is not enough. Alerts get missed, go to the wrong person, or arrive during a holiday. Use a staged alert strategy:
Manual expiry checks
Check a live domain's certificate expiry:
echo | openssl s_client -servername example.com \ -connect example.com:443 2>/dev/null \ | openssl x509 -noout -dates
Check a local certificate file:
openssl x509 -in cert.pem -noout -dates
Check all certificates in a JKS keystore:
keytool -list -v -keystore keystore.jks -storepass changeit \ | grep -E "Alias|Valid from|until"
Script to check multiple domains:
#!/bin/bash
DOMAINS=("example.com" "api.example.com" "internal.company.com")
WARNING_DAYS=30
for domain in "${DOMAINS[@]}"; do
expiry=$(echo | openssl s_client -servername $domain \
-connect $domain:443 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null \
| cut -d= -f2)
expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$expiry" +%s)
now_epoch=$(date +%s)
days_left=$(( ($expiry_epoch - $now_epoch) / 86400 ))
echo "$domain: $days_left days remaining (expires $expiry)"
done
Automated monitoring approaches
Prometheus + Blackbox Exporter — if you're already running Prometheus, the Blackbox Exporter has a built-in SSL certificate expiry probe. Add an alert rule:
- alert: SSLCertExpiringSoon
expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 30
for: 1h
labels:
severity: warning
annotations:
summary: "SSL cert expiring in less than 30 days — {{ $labels.instance }}"
Nagios / Icinga — use the check_http plugin with the --ssl and --certificate flags to alert on expiry.
Cron-based script — run the shell script above daily via cron and send results to Slack or email.
Using CertLens Monitor
CertLens has a built-in certificate monitor that scans your domains on a schedule and alerts you before certificates expire — no setup beyond adding your domain.
CI/CD pipeline checks
Add a certificate check to your deployment pipeline so an expiring certificate blocks the release and forces a renewal:
#!/bin/bash
# check-cert-expiry.sh — add to CI pipeline
DOMAIN="$1"
MIN_DAYS="${2:-30}"
expiry=$(echo | openssl s_client -servername $DOMAIN \
-connect $DOMAIN:443 2>/dev/null \
| openssl x509 -noout -checkend $(($MIN_DAYS * 86400)))
if [ $? -ne 0 ]; then
echo "ERROR: Certificate for $DOMAIN expires within $MIN_DAYS days. Renew before deploying."
exit 1
fi
echo "OK: Certificate for $DOMAIN is valid for more than $MIN_DAYS days."
Add to your GitHub Actions workflow:
- name: Check certificate expiry run: ./check-cert-expiry.sh api.example.com 30