- Renewal: `/etc/cron.weekly/letsencrypt` → `certbot renew`, **webroot** at `/var/spool/nginx/tmp/client` (conf: `/etc/letsencrypt/renewal/mail.etersoft.ru.conf`). **nginx MUST be up** for webroot — do NOT stop it around renew (leftover `serv nginx stop` from old standalone auth silently broke renewals: "All renewals failed").
- Deploy hook: `/etc/letsencrypt/renewal-hooks/deploy/update_pem.sh` (symlink → `/etc/postfix/tls/update_pem.sh`). Runs after EVERY successful renewal and pushes the live cert into the mail daemons:
- Postfix — ONE combined cert+key file `/etc/postfix/tls/mail.etersoft.ru_full.pem` (`smtpd_tls_cert_file = smtpd_tls_key_file` = same path in `main.cf`)
- Cyrus — cert `/var/lib/imap/ssl/mail.etersoft.ru.crt`, key `/var/lib/imap/ssl/etersoft.pem` (`tls_server_cert`/`tls_server_key` in `/etc/imapd.conf`)
- reloads postfix, cyrus-imapd, nginx.
- nginx reads live `fullchain.pem` directly (`/etc/nginx/sites-enabled.d/mail.etersoft.ru.conf`).
### Check / diagnose
- Served vs live: `openssl s_client -connect mail.etersoft.ru:993 -servername mail.etersoft.ru </dev/null | openssl x509 -noout -dates` vs `certbot certificates`. Divergent dates = daemon copies stale (deploy hook didn't run).
- Hook must live in `/etc/letsencrypt/renewal-hooks/deploy/` (fires on any renewal incl. manual) — NOT only as deprecated `--renew-hook` on the cron line (manual `certbot --expand` would skip it → mail stuck on expired cert).
- Force fresh deploy: `certbot renew` (nginx up) — hook fires and redeploys.
**NEVER change this!** The `H` prefix is intentional. When defaultdomain matches login domain, Cyrus strips domain from userid → SASL gets username without @domain → auth breaks. `Hoffice` doesn't match any real domain → full userid preserved.
**NEVER change this!** The `H` prefix is intentional. When defaultdomain matches login domain, Cyrus strips domain from userid → SASL gets username without @domain → auth breaks. `Hoffice` doesn't match any real domain → full userid preserved.