Runbook — CertbotRenewalStalled / CertbotRenewalFailedRecently / PushgatewayDown
Alerts covered
Section titled “Alerts covered”| Alert | Severity | Audience | Source |
|---|---|---|---|
CertbotRenewalStalled | warning | ops | (time() - certbot_renewal_last_success_timestamp) > 86400 |
CertbotRenewalFailedRecently | critical | ops | last_failure_timestamp > last_success_timestamp |
PushgatewayDown | warning | ops | up{job="pushgateway"} == 0 |
What this means
Section titled “What this means”A bash script (infrastructure/monitoring/scripts/certbot-renewal-check.sh) runs:
- Every 6 hours via cron (
certbot-check.cron). - On every successful certbot renewal via
--deploy-hook.
On each run it executes certbot renew --dry-run, then POSTs one of two metrics per domain to the Prometheus pushgateway:
certbot_renewal_last_success_timestamp{domain="hub.innoqualis.com"} <epoch>certbot_renewal_last_failure_timestamp{domain="hub.innoqualis.com", reason="acme_challenge_failed"} <epoch>The rules then fire on staleness or precedence between the two timestamps.
CertbotRenewalStalled is the heartbeat-missing warning — the script itself stopped running (cron broken, container down, network from script to pushgateway broken). CertbotRenewalFailedRecently is the precondition-precise alert — the script ran AND certbot’s renewal pipeline returned an error. PushgatewayDown is the meta-alert that catches the case where the carrier infrastructure for both above alerts has itself failed.
Diagnosis
Section titled “Diagnosis”CertbotRenewalStalled (warning)
Section titled “CertbotRenewalStalled (warning)”# 1. Check whether the script is being invoked at all (cron health)sudo journalctl -t certbot-renewal-check --since "-1d"
# 2. Manually invoke the script to see what failssudo /usr/local/bin/certbot-renewal-check.shecho "exit code: $?"# 0 = renewed/dry-run OK# 1 = certbot failed; failure metric pushed# 2 = pushgateway unreachable# 3 = certbot binary missing
# 3. Inspect the most recent certbot logsudo tail -200 /tmp/certbot-renew.logCertbotRenewalFailedRecently (critical)
Section titled “CertbotRenewalFailedRecently (critical)”# Inspect the labelled reason on the alert payload (in the Alertmanager email):# reason="acme_challenge_failed" → step 1 below (the 2026-05-17 cause)# reason="rate_limited" → step 2# reason="connection_error" → step 3# reason="dry_run_failed" (generic) → walk all three
# 1. ACME challenge path reachable? (2026-05-17 incident cause)curl -fsS -o /dev/null -w '%{http_code}\n' \ http://hub.innoqualis.com/.well-known/acme-challenge/test# Expected: 404 (challenge file genuinely doesn't exist).# 301 to HTTPS = nginx ACME block dropped — restore in nginx config.# 502/503 = upstream broken before we reach the challenge handler.
# 2. Let's Encrypt rate limits hit?# Inspect /tmp/certbot-renew.log for "too many" / "rate limit" lines.# https://letsencrypt.org/docs/rate-limits/
# 3. Outbound connectivity to Let's Encrypt?curl -fsS -o /dev/null -w '%{http_code}\n' https://acme-v02.api.letsencrypt.org/directory# Expected: 200.
# 4. Inspect certbot's view of the cert lineagesudo certbot certificates
# 5. Most recent renewal attemptsudo docker logs --tail 500 innoqualis-certbot 2>&1 | grep -iE 'renew|error|fail' | tail -50PushgatewayDown (warning)
Section titled “PushgatewayDown (warning)”# 1. Container statussudo docker ps --filter name=eqms-pushgateway
# 2. Recent logssudo docker logs --tail 100 eqms-pushgateway
# 3. Direct health probecurl -fsS http://localhost:9091/-/healthy
# 4. Restart (persistence volume preserves state)cd /opt/innoqualis/monitoring && docker compose up -d pushgatewayRecovery
Section titled “Recovery”| Symptom | Action |
|---|---|
| ACME challenge 301-redirects to HTTPS | Restore the location /.well-known/acme-challenge/ block in nginx port-80 server (2026-05-17 root cause) |
| Let’s Encrypt rate limit | Wait for the rate window (one week); do NOT force-renew |
| ACME directory unreachable | Check VPS outbound firewall + DNS resolution |
| Certbot binary missing / corrupt | apt install --reinstall certbot or rebuild the certbot container |
| Pushgateway crashed | docker compose up -d pushgateway (persistence preserves last-success timestamps) |
| Cron not running | systemctl status cron; check /etc/cron.d/certbot-renewal-check perms (must be 0644 and owned by root) |
After remediation, force a renewal probe so the success timestamp advances immediately rather than waiting for the next 6h cron tick:
sudo /usr/local/bin/certbot-renewal-check.shKnown false-positive scenarios
Section titled “Known false-positive scenarios”- Initial deployment / fresh certbot install — no success timestamp yet;
CertbotRenewalFailedRecentlycannot fire (itsand last_success > 0clause guards this), butCertbotRenewalStalledwill fire once the 24h window expires unless the first successful probe runs. Mitigation: run the script manually as part of the deploy procedure to seed the metric. - Pushgateway restart — without the persistence volume the last-success timestamp resets to zero on restart. Spec 30.4 configures persistence (
--persistence.file=/data/persistence+pushgateway_datanamed volume) precisely to avoid this; if persistence is disabled (e.g. local dev), expect a 24h-after-restart false fire. - Certbot lineage rename — if a cert is moved (e.g.
hub.innoqualis.comlineage rotated toweb.innoqualis.com) the old domain’s timestamp goes stale forever. ManuallyDELETE /metrics/job/certbot-renewal/instance/<old-domain>against the pushgateway to retire the series.
Escalation
Section titled “Escalation”- Acknowledge in the control panel
/alertspage (silences for 4h while you investigate). - If neither the cron nor a manual invocation can produce a fresh success timestamp within an hour, escalate to the on-call inbox (
contact@innoqualis.com) and consider a manualcertbot renew --force-renewalto extend the cert window while debugging. - If the 30.2
SSLCertNearExpiryrule has also fired on the same domain, the cert is now within 30 days — the situation is no longer purely cause-side and you have a hard deadline.
Compliance note
Section titled “Compliance note”This alert + its successful runbook execution are evidence of “documented procedure for responding to system anomalies” per 21 CFR Part 11 §11.10(k). The pushgateway timestamp series provides the audit trail of renewal liveness over time.
Related
Section titled “Related”- Phase 30.4 spec
- Phase 30.2 spec — consequence-side defence
tls-cert-expiring-30d— first warning, 30 days before expirytls-cert-expiring-7d— hard-deadline warning- 2026-05-17 incident post-mortem (in
tasks/lessons.md)
Last reviewed
Section titled “Last reviewed”2026-05-29 — shipped with Spec 30.4 / KAN-294