@@ -262,6 +262,10 @@ process_domain() {
262262 legoman.py auto --domain " $domain " ${ACME_EMAIL: +--email " $ACME_EMAIL " }
263263}
264264
265+ # Set when certificates have been renewed but not yet applied to haproxy.
266+ # Survives across passes, so a failed apply is retried on the next one.
267+ APPLY_PENDING=false
268+
265269# One pass over every domain: make sure the records exist, then issue or renew.
266270# Idempotent, so the first pass and every later one are the same code. There is
267271# deliberately only one of these -- an earlier split between a one-shot
@@ -285,9 +289,24 @@ run_pass() {
285289 done <<< " $(get-all-domains.sh)"
286290
287291 if [ " $changed " -eq 1 ]; then
288- collect_evidence || echo " Evidence generation failed" >&2
289- build_combined_pems || echo " Combined PEM build failed" >&2
290- haproxy_reload || true
292+ APPLY_PENDING=true
293+ fi
294+
295+ # Applying is separate from renewing, and retried until it sticks. A
296+ # renewal that reaches disk but not haproxy is invisible: the next pass
297+ # sees a fresh certificate, reports "nothing to renew", and never reloads
298+ # again -- so haproxy would serve the pre-renewal certificate until it
299+ # expires, which is soon, because being near expiry is why it renewed.
300+ if [ " $APPLY_PENDING " = true ]; then
301+ local applied=true
302+ collect_evidence || { echo " Evidence generation failed" >&2 ; applied=false; }
303+ build_combined_pems || { echo " Combined PEM build failed" >&2 ; applied=false; }
304+ haproxy_reload || applied=false
305+ if [ " $applied " = true ]; then
306+ APPLY_PENDING=false
307+ else
308+ echo " [$( date) ] Certificate apply incomplete; retrying on the next pass" >&2
309+ fi
291310 fi
292311 FIRST_PASS=false
293312 [ " $failed " -eq 0 ]
0 commit comments