Use Case

Monitor SSL Certificate Renewal

When your SSL cert expires, everything breaks. Users see scary warnings. Traffic drops. Trust evaporates.

The Problem

Let's Encrypt certificates expire every 90 days. Certbot runs automatically to renew them. Usually.

But things go wrong:

  • Certbot fails silently (rate limits, DNS issues, webroot problems)
  • Server changes break the renewal hook
  • Cron daemon stopped running after a reboot
  • Someone deleted the certbot cron entry
  • Nginx/Apache didn't reload after renewal

You find out when customers complain about the "Not Secure" warning — or worse, when Google de-indexes your site.

The Solution

Monitor the renewal process, not just the certificate expiry. Get alerted immediately when renewal fails, with enough time to fix it manually.

Basic Certbot Monitoring

Wrap your certbot renewal with a heartbeat ping:

#!/bin/bash
# /opt/scripts/renew-certs.sh

set -e

# Attempt renewal
certbot renew --quiet

# Reload web server
systemctl reload nginx

# Ping CronSignal on success
curl -fsS https://api.cronsignal.io/ping/YOUR_CHECK_ID

Crontab entry (run twice daily as recommended by Let's Encrypt):

0 0,12 * * * /opt/scripts/renew-certs.sh >> /var/log/certbot-renew.log 2>&1

With Certificate Validation

Go further — verify the certificate is actually valid after renewal:

#!/bin/bash
# /opt/scripts/renew-certs.sh

set -e

# Attempt renewal
certbot renew --quiet

# Reload nginx
systemctl reload nginx

# Wait for nginx to fully reload
sleep 5

# Verify the certificate is valid and not expiring soon
EXPIRY=$(echo | openssl s_client -connect localhost:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))

if [ $DAYS_LEFT -lt 7 ]; then
    echo "Certificate expires in $DAYS_LEFT days - too soon!"
    exit 1
fi

echo "Certificate valid for $DAYS_LEFT more days"

# All checks passed - ping
curl -fsS https://api.cronsignal.io/ping/YOUR_CHECK_ID

Multi-Domain Setup

If you manage certificates for multiple domains, verify each one:

#!/bin/bash
set -e

DOMAINS="example.com api.example.com blog.example.com"

certbot renew --quiet
systemctl reload nginx
sleep 5

for DOMAIN in $DOMAINS; do
    DAYS_LEFT=$(echo | openssl s_client -connect $DOMAIN:443 -servername $DOMAIN 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2 | xargs -I {} date -d {} +%s | xargs -I {} echo "(( {} - $(date +%s) )) / 86400" | bc)

    if [ $DAYS_LEFT -lt 7 ]; then
        echo "ALERT: $DOMAIN expires in $DAYS_LEFT days"
        exit 1
    fi
    echo "$DOMAIN: $DAYS_LEFT days remaining"
done

curl -fsS https://api.cronsignal.io/ping/YOUR_CHECK_ID

Kubernetes with cert-manager

If you use cert-manager in Kubernetes, monitor certificate status with a CronJob:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: cert-monitor
spec:
  schedule: "0 */6 * * *"  # Every 6 hours
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: cert-check
            image: bitnami/kubectl:latest
            command:
            - /bin/sh
            - -c
            - |
              # Check all certificates are ready
              NOT_READY=$(kubectl get certificates -A -o jsonpath='{.items[?(@.status.conditions[0].status!="True")].metadata.name}')

              if [ -n "$NOT_READY" ]; then
                echo "Certificates not ready: $NOT_READY"
                exit 1
              fi

              echo "All certificates healthy"
              curl -fsS https://api.cronsignal.io/ping/YOUR_CHECK_ID
          restartPolicy: OnFailure

Setting the Schedule

For Let's Encrypt certificates:

  • Run renewal: Twice daily (0:00 and 12:00)
  • CronSignal schedule: Every 12 hours
  • Grace period: 1 hour

This gives you ~60 days of warning before expiry (certificates renew at 30 days before expiry).

What Gets Caught

This monitoring setup alerts you when:

  • Certbot fails: Rate limits, authorization failures, webroot issues
  • Web server doesn't reload: Config errors, systemd issues
  • Certificate isn't applied: Renewal succeeded but nginx still serves old cert
  • Cron doesn't run: Server down, cron disabled, entry deleted
  • Script errors: Permission issues, missing dependencies

Common Certbot Failures

Webroot Challenge Fails

Your web server config may block /.well-known/acme-challenge/. Ensure this location is accessible.

Rate Limits

Let's Encrypt has rate limits. If you hit them, renewal fails. Use --dry-run for testing.

DNS Propagation

For DNS-01 challenges, DNS may not propagate in time. Use --dns-propagation-seconds 60.

Post-Hook Failures

If your --post-hook script fails, certbot may report success but your web server isn't reloaded. Always test hooks manually.

Never let an SSL cert expire

Get alerted the moment your renewal process fails — weeks before the certificate actually expires.

Start Monitoring Free

3 checks free. No credit card required.