Skip to main content

Automatic TLS Without a Hosted Edge: The Real Ops Bill for Custom Domains and Wildcards on a Self-Hosted Fleet

11 min readDora NodaDora Noda
Share
On this page

Yesterday, Let's Encrypt's staging environment started issuing 64-day certificates — the rehearsal for a production cutover on February 10, 2027, when the default lifetime drops from 90 days to 64, then to 45 days in February 2028. If your platform sits behind Cloudflare, Vercel, or any hosted edge, that sentence is trivia: cert rotation is a vendor SLA you never see. If you terminate TLS on machines you own, it is a bill coming due — denominated not in dollars but in renewal events, challenge infrastructure, and runbook toil.

This post itemizes that bill for a concrete fleet: 50 tenants with custom domains plus one wildcard for platform subdomains, running on owned Hetzner nodes with no hosted edge in front. It compares the three TLS paths worth running there — Caddy's automatic HTTPS, Traefik with cert-manager, and Envoy's SDS — decides HTTP-01 vs DNS-01 behind Gateway API, and condenses the renewal runbook you must get right before February.

Where this post sits: last month we compared Caddy vs cert-manager vs managed LB certificates through Hetzner's July load balancer disruption, and this month we published a four-step cert-manager audit plus the 4-month proof plan for the 64-day cutover. Those cover whether your renewal loop survives the shrink; this one prices what the loop costs to own — and which machinery to own it with.

The bill, up front​

Every ACME renewal is a transaction: schedule it, validate domain control, fetch the cert, deploy it, reload the listener, confirm it serves. The standard schedule renews at roughly two-thirds of lifetime, so each lifetime step-up multiplies the transactions per cert per year:

LifetimeRenews everyRenewals per cert per yearHeadroom after renewal starts
90 days~60 days~6~30 days
64 days~43 days~8–9~21 days
45 days~30 days~12~15 days
6 days (shortlived)~4 days~91~2 days

Multiply by a 50-tenant fleet holding 50 custom-domain certificates plus one wildcard — 51 certs:

LifetimeFleet renewal events per yearPer day
90 days~306~0.8
64 days~434~1.2
45 days~620~1.7
6 days~4,600~12.6

That is the number the hosted edge absorbs for you. At 90-day lifetimes your fleet renewed a cert most days; at 45 days it renews nearly two a day, each with half the headroom; at 6-day lifetimes — generally available since January 2026 for the shortlived profile — renewal becomes a continuous background process firing a dozen times daily. The rest of this post is about what each of those events demands from your infrastructure, and which of three paths meets the demand cheapest.

The challenge decision comes first​

Before picking software, pick the challenge type, because it constrains everything downstream. ACME offers two practical paths to prove domain control, and behind Gateway API they behave very differently:

DimensionHTTP-01DNS-01
Wildcard certsNo — wildcards require DNS-01Yes, the only way
Custom tenant domainsWorks if port 80 reaches your solver for each domainRequires DNS control per domain (customer CNAME delegation to your validation zone)
Behind Gateway APIcert-manager's solver mints temporary routes; needs HTTP listener + port 80 openNo ingress path needed; needs a DNS provider webhook or RFC 2136/ACME-DNS hookup
Moving partsSolver pods + temporary HTTPRoute/Ingress objects per orderDNS credentials, propagation waits, TTL tuning
Failure shapeFails closed and loudly (unreachable solver)Fails slowly (propagation timeouts, stale TXT records)

Two rules fall out. First, any fleet serving a wildcard — *.app.yourplatform.com for preview or tenant subdomains — runs DNS-01 at least for that cert, no matter what the custom domains use. Second, per-tenant custom domains (app.customer.com CNAME'd to your fleet) favor HTTP-01 operationally: nothing to ask of the customer's DNS beyond the CNAME they already created, and validation rides the same port 80 your HTTP listener already serves.

The shrinking schedule taxes both paths. The February 2027 cutover also shortens authorization reuse from 30 days to 10, then to 7 hours in 2028 — the validation cache your flows silently rely on evaporates, so full challenge validations repeat far more often. HTTP-01 pays in solver traffic; DNS-01 pays in TXT-record churn and propagation waits, where details like explicit TTL settings already bite (our September post covers Hetzner's mandatory ttl handling). Budget challenge capacity for the 10-day-reuse world, not the 30-day one.

Path 1: Caddy's automatic HTTPS​

Caddy inverted TLS responsibility: you configure hostnames, and the server obtains, installs, and renews certificates itself over ACME with zero configuration. Point a reverse_proxy at your app on ports 80/443 and the first handshake triggers issuance; renewals run automatically with no cron to maintain. For a single box, this is the cheapest correct TLS in existence — and the renewal loop lives on your machines, unreachable by any provider load balancer outage.

The fleet bill has two line items. First, storage: certificates and ACME state live in Caddy's storage backend, file-based by default. One instance is trivially consistent; two instances behind round-robin DNS with separate stores duplicate issuance, race renewals, and can trip CA rate limits. Past one node, every instance must share storage — a volume, Consul, Redis, or a database backend — so the fleet acts as one ACME client.

Second, On-Demand TLS: issuance at handshake time for never-before-seen hostnames is exactly what multi-tenant custom domains want, and exactly what an attacker abuses to turn your fleet into an open issuance oracle that burns its rate-limit budget. Caddy ships the guardrails — an ask endpoint to approve hostnames, plus rate limits — but you must build and operate the ask service, which is now in your certificate path with its own availability requirement.

Verdict: Caddy carries you from one box to roughly a dozen nodes with shared storage and a gated ask endpoint. Past that, per-tenant issuance policy re-implements the issuer/solver separation cert-manager gives you declaratively — which is Path 2.

Path 2: Traefik with cert-manager​

This path splits the job: cert-manager owns issuance and renewal as declarative API objects (Issuer/ClusterIssuer, Certificate, solvers), while Traefik owns termination and routing, increasingly through its Gateway API provider rather than Ingress annotations. The Gateway TLS config references cert-manager-managed Secrets on typed listeners; renewal updates the Secret and Traefik picks it up.

The split is the feature. Issuance policy (which CA, which challenge, which namespaces may request) lives in cert-manager objects with RBAC and audit trails, while Traefik's Gateway API support keeps maturing — including 2026 upstream work like multi-certificate listeners and the Traefik 3.7 fix for BackendTLSPolicy root-CA handling that had stalled Gateway API migrations. One team can reason about CAs while another reasons about routing, and neither touches the other's CRDs.

The bill is two control loops to upgrade, monitor, and debug instead of one. cert-manager releases move fast (v1.21 added ACME Renewal Information behind the ACMEUseARI gate), Traefik's Gateway API provider still trails its mature Ingress provider in edge-case coverage, and failures now require asking "which loop broke?" — did issuance stall (cert-manager events, ACME orders) or did pickup stall (Traefik listener status, Secret references)? For a multi-tenant fleet this is usually the right trade: the separation that costs a second dashboard is the same separation that lets tenant onboarding mint Certificate objects without touching the edge config.

Path 3: Envoy with SDS​

Envoy's Secret Discovery Service decouples rotation from reload: instead of restarting or reconfiguring the proxy when a cert changes, Envoy watches — via file-based SDS (watched_directory over mounted Secrets) or the xDS API — and hot-swaps the new certificate with no listener rebuild and no dropped connections. Pair it with cert-manager writing renewed certs into Secrets (directly, or via Envoy Gateway's Gateway API integration), and rotation becomes a data update rather than a deployment event.

At 6-day lifetimes this is the cleanest rotation story of the three: a dozen renewal events a day that each resolve as a secret swap, not twelve reloads. But SDS has a genuine gotcha to budget for — propagation is only as reliable as the watch. Operators have reported Envoy Gateway pods serving a stale cert until restart because an SDS/Secret rotation never propagated to the running proxy, in one case expiring a cert while the renewed Secret sat ready in the cluster. The fix is mechanical but mandatory: monitor served-certificate expiry from outside the cluster (not just Secret freshness inside it), and add a targeted guard — a controller or cron check that rolling-restarts the Envoy Deployment when its consumed Secret content changes.

Verdict: Envoy SDS is the most machinery — xDS concepts, Gateway API or raw config, plus the propagation guard — in exchange for the only rotation path where the data plane never blinks. Choose it when connection continuity during rotation is a requirement, not a nicety.

The renewal runbook, condensed​

Whichever path terminates TLS, the same six runbook items keep 620 renewal events a year from becoming an incident. Each is covered in depth in the audit and the proof plan; this is the checklist form:

  1. Grep for hardcoded renewal offsets. Any 60, 80, or 83 meaning "days into a 90-day cert" in cron, wrappers, or runbooks — plus absolute renewBefore values computed against 90 days — is a bug with a February trigger date.
  2. Move scheduling to ARI or a lifetime-relative rule. Enable cert-manager's ACMEUseARI gate (v1.21+, alpha, off by default) so the CA picks renewal windows; otherwise renew at two-thirds of issued lifetime, never at a fixed day count.
  3. Run staging-canary renewals now. Staging has issued 64-day certs since yesterday — force a full canary cycle (issue, renew, deploy, reload, serve) there before production moves.
  4. Retry across multi-hour CA outages. October 12's ~6-hour ACME issuance disruption is the failure shape: backoff-and-retry renewals across it, and page on failed renewal attempts, not just on expiry.
  5. Line up a second ACME CA. Wildcard and tenant-facing issuance need a fallback CA configured before February, not researched during an outage.
  6. Alert on served expiry, from outside. Check what the edge handshake presents — Secret freshness and order success can both lie while clients see an expiring cert (the SDS gotcha above is one way; stale mounts are another).

The bill, totaled​

Side by side, for the 50-tenant fleet at 45-day lifetimes (~620 renewal events/year):

DimensionCaddy automatic HTTPSTraefik + cert-managerEnvoy SDS
Renewal events handled byCaddy itself, in-bandcert-manager controllerscert-manager + SDS propagation
Challenge infraBuilt-in HTTP-01; DNS plugins per provider for wildcardsSolver pods (HTTP-01) and/or DNS webhooks (DNS-01)Same as cert-manager (SDS is rotation, not issuance)
Rotation mechanicsIn-process, automaticSecret update + Traefik pickupHot secret swap, no restart — plus propagation guard
Failure modesSplit-brain issuance without shared storage; open oracle without ask gateTwo loops to triage; provider-version skewStale served cert if the watch misses; most config surface
Day-to-day toilLowest (one binary + ask service)Medium (two upgrades, two dashboards)Highest (xDS + guard)
Fleet-size fit1 to ~12 nodes~5 nodes and up, multi-tenantFleets where rotation must never blink

And what the hosted edge absorbs as vendor SLA: all 620 events, the challenge infrastructure, the retry-across-outage logic, the fallback CA, and the 3 a.m. expiry page. That absorption is genuinely valuable — it is also a coupling, as July's Hetzner load balancer disruption showed for tenants whose renewal path shared fate with the provider's data plane. Owning the certificate path means owning the bill above; the compensation is that no provider incident can stall a renewal loop that lives entirely on your machines.

Pick by fleet shape, not by fashion. A single box or small static set of domains: Caddy, and stop reading. A growing multi-tenant fleet where onboarding must not touch edge config: Traefik with cert-manager behind Gateway API. A fleet where every rotation must be hitless and auditable: Envoy SDS with the propagation guard. In all three cases, run the six-item runbook before February 10 — the staging rehearsal is live, the calendar is fixed, and the bill comes due whether you itemized it or not.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex