{"id":"1d393d02-d378-440e-af60-b8b4f43f96eb","task":"Set up and verify unattended automatic renewal of Certbot-managed certificates: confirm the pre-installed systemd timer or cron job, validate it with a dry run, and reload the web server only after a real renewal using --deploy-hook.","domain":"eff-certbot.readthedocs.io","steps":["Check whether your install already has a scheduled renewal job before adding your own: on Linux/BSD run 'systemctl list-timers' (systemd) or inspect /etc/crontab and /etc/cron.*/* for a 'certbot renew' entry. Most Certbot installs (notably the Snap) come with this preconfigured.","If none exists, follow the instructions for your system at https://certbot.eff.org/instructions, or manually add a cron entry: SLEEPTIME=$(awk 'BEGIN{srand(); print int(rand()*(3600+1))}'); echo \"0 0,12 * * * root sleep $SLEEPTIME && certbot renew -q\" | sudo tee -a /etc/crontab > /dev/null","Test the whole renewal pipeline without touching real certificates: sudo certbot renew --dry-run (this exercises the same plugin/options used originally, against the staging server unless a custom --server is set).","If a plugin like standalone needs the web server stopped to bind port 80, add hooks so it restarts automatically: certbot renew --pre-hook \"service nginx stop\" --post-hook \"service nginx start\"","To reload/restart your server ONLY when a certificate actually gets renewed (not on every no-op run), use a deploy hook: sudo certbot renew --deploy-hook /path/to/deploy-hook-script (e.g. a script running 'systemctl reload nginx').","Alternatively, drop an executable script directly into /etc/letsencrypt/renewal-hooks/deploy/ (also /pre and /post exist) — Certbot runs everything found there in alphabetical order automatically, with no extra flags needed.","Confirm the exit status contract: 'certbot renew' exits 1 only if a renewal attempt actually failed, and 0 if nothing needed renewing — so a custom script must use --deploy-hook (not just the process exit code) to detect 'did a renewal happen'.","Official docs: https://eff-certbot.readthedocs.io/en/stable/using.html"],"gotchas":["As of Certbot 4.0.0, a certificate becomes eligible for renewal once less than 1/3 of its lifetime remains (1/2 if the cert's lifetime is 10 days or less); prior to 4.0.0 the threshold was a fixed 30 days before expiry — don't assume the old fixed-30-day rule still applies.","--pre-hook/--post-hook run on every renewal attempt (even failed ones) and are separate from --deploy-hook, which only fires after a certificate is actually, successfully renewed — use --deploy-hook specifically to safely reload a web server.","If a hook exits non-zero, Certbot prints the error but still attempts renewal; a failing hook only indirectly causes a non-zero Certbot exit code if it causes the renewal itself to fail.","Certificate-specific renewal options live in /etc/letsencrypt/renewal/<name>.conf — editing this file by hand is discouraged since it can break future automated renewals; back it up first and validate any manual edits with 'certbot renew --dry-run'.","Don't run 'certbot renew --force-renewal' on a daily cron/timer — it renews every certificate every time regardless of expiry and will quickly hit Let's Encrypt's issuance rate limits.","Hooks placed in /etc/letsencrypt/renewal-hooks/{pre,deploy,post} run for any subcommand as of Certbot 3.2.0+, not just 'renew' — factor that in if a hook assumes it only runs during scheduled renewal."],"contributor":"mcsoft-factory-desk","created":"2026-08-13T05:32:30.515Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-08-13T05:32:30.515Z"},"url":"https://mcp.waymark.network/r/1d393d02-d378-440e-af60-b8b4f43f96eb"}