PEPAI / tools

acme-renew-doctor

Explain why a Certbot renewal failed, or quietly never ran, from files you pass.

The problem

Let's Encrypt stopped expiration-notification emails in June 2025, so a failed renewal or a timer that quietly stops is easy to miss until the certificate expires.

The cause sits in one of three layers: the scheduler, the ACME validation or the deployment. acme-renew-doctor reads the evidence you saved and names that layer, with the line that proves it.

Example output

$ acme-renew-doctor example.com --from-log tests/fixtures/certbot/http01-timeout-repeated.log --from-timer-status tests/fixtures/timer/certbot-timer-disabled.txt
acme-renew-doctor 0.1.0 (read-only, nothing was changed)
target: example.com · client: auto
inputs: log=tests/fixtures/certbot/http01-timeout-repeated.log · timer status=tests/fixtures/timer/certbot-timer-disabled.txt · cert disk=(none) · cert live=(none)

✗ LAYER1_TIMER_DISABLED  confidence=high  layer=scheduler
  evidence:
  • [timer] UnitFileState=disabled
  • [timer] ActiveState=inactive

✗ LAYER2_HTTP01_TIMEOUT  confidence=high  layer=validation
  evidence:
  • [log] line 68: 203.0.113.10: Fetching http://example.com/.well-known/acme-challenge/timeouttoken0000000000000000001: Timeout during connect (likely firewall problem)
  • [log] 2 attempts in this log (ACME response and Certbot summary of one attempt count as one), first on line 20

Excerpt of real output on synthetic test fixtures from the repository (the suggested next checks are cut off here).

Install

cargo install --locked acme-renew-doctor

Needs Rust 1.85 or newer. Ready-made binaries for Linux, Windows and macOS are on the releases page.

What it deliberately does not do

Links

GitHubcrates.ioQuality
← Back to all tools