Extracts the file-based deploy target connectors: - haproxy.md (107 lines) — combined-PEM (cert+chain+key) deploy with haproxy -c validate; multi-frontend + crt-list directory guidance - traefik.md (105 lines) — file-provider zero-reload deploy; file watcher latency notes; mixing with built-in ACME guidance - caddy.md (100 lines) — admin API mode (recommended) vs file mode; admin-API exposure threat model - envoy.md (112 lines) — file SDS mode (recommended) vs static bootstrap; service-mesh interactions - postfix.md (175 lines) — dual-mode (Postfix MTA / Dovecot IMAPS) connector with daemon-specific quirks (STARTTLS chain expectations, no shared session cache); Bundle 11 test pins Index forward-list expanded to enumerate all 10 target connectors (5 from Phase 4 structural + 5 from this batch) in alphabetical order. This is part 3 of 4 for the Phase 4 follow-on (per-connector page extraction) tracked in cowork/docs-overhaul-phase-2-restructure-2026-05-04/log.md. Net add: 5 files, 599 lines. No content removed from index.md.
4.0 KiB
HAProxy Connector — Operator Deep-Dive
Last reviewed: 2026-05-05
Operator-grade documentation for the HAProxy target connector. For the connector-development context (interface contract, registry, atomic deploy primitive shared across all targets), see the connector index.
Overview
HAProxy differs from NGINX and Apache in one important way: it expects all TLS material in a single combined PEM file — certificate, intermediate chain, and private key concatenated. The connector builds this combined file, writes it with 0600 permissions (since it contains the private key), optionally validates the HAProxy configuration, and reloads.
Implementation lives at internal/connector/target/haproxy/.
When to use this connector
Use the HAProxy connector when:
- HAProxy fronts your applications and you want certctl to rotate the cert + chain + key in place atomically without hand-rolling the combined-PEM build.
- You want validate-before-reload behaviour to keep a bad config from taking down the load balancer mid-rotation.
Look elsewhere when:
- You're running HAProxy Enterprise's hot-cert-update API path — the connector currently uses the file-write-and-reload model; the API path is on the V3-Pro roadmap.
- You're not running HAProxy directly but a managed load balancer (AWS ALB, Azure Application Gateway). Use the cloud-native target connector for that platform instead.
Configuration
{
"pem_path": "/etc/haproxy/certs/site.pem",
"reload_command": "systemctl reload haproxy",
"validate_command": "haproxy -c -f /etc/haproxy/haproxy.cfg"
}
The combined PEM is built in this order: server certificate, intermediate / chain certificates, private key.
The validate_command is optional — if omitted, the connector
skips config validation and goes straight to reload. Keeping it
on is the production-recommended posture.
Deploy contract
Every cert deploy follows the Bundle I deploy.Apply(ctx, plan)
flow:
- Idempotency check — SHA-256 over the combined PEM bytes; skip if the destination already matches.
- Pre-deploy backup — copy existing PEM to
<pem_path>.certctl-bak.<unix-nanos>. - Atomic write — temp-file + chown + atomic rename.
- PreCommit (validate) — runs
haproxy -c -f /etc/haproxy/haproxy.cfg. Failure aborts; no live cert touched. - Atomic rename — temp → final.
- PostCommit (reload) — runs
systemctl reload haproxy(or the operator's override). - Post-deploy TLS verify — dials the configured endpoint when configured; pulls leaf cert SHA-256; compares against deployed bytes. Mismatch triggers automatic rollback.
Operator playbook
Old cert served via session resumption
HAProxy keeps TLS sessions alive for the configured
tune.ssl.lifetime (default 1h). Resumed clients see the OLD
cert until their session expires. Post-deploy verify in certctl
returns the NEW cert from a fresh handshake; warm clients see the
OLD cert until session expiration.
Multi-frontend deployments
When HAProxy serves multiple frontends with different certs,
configure one target per frontend's cert in the certctl
control plane. Each gets its own pem_path. The reload command
is shared (HAProxy reloads all frontends together), so the
deploys can land in any order; the final reload picks them all up.
crt-list directories
If your HAProxy config uses a crt-list directory rather than a
single PEM, set pem_path to a file inside the directory and let
HAProxy enumerate it on reload. The connector treats pem_path
as a single file regardless of HAProxy's directory semantics.
Related docs
- Connector index — interface contract, registry, deploy primitive
- NGINX — separate-file deploy contract counterpart
- Apache — separate-file deploy contract with
apachectl configtest - Migration: ACME from HAProxy — pattern for pointing edge proxies at certctl's ACME server (Caddy walkthrough; HAProxy ACME plumbing is similar)