Automatic Certificate Issuance & Renewal
Relevant source files
The following files were used as context for generating this wiki page:
Kamal-proxy implements an automated system for managing TLS certificates via the ACME protocol. The system is designed to handle both static deploy-registered hosts and dynamic domains learned at runtime. It features Subject Alternative Name (SAN) batching to group up to 100 domains into single certificates, reducing the frequency of ACME orders and staying within rate limits.
The architecture supports multiple challenge types (HTTP-01 and DNS-01), wildcard certificates, and background renewal loops that leverage ACME Renewal Information (ARI) where available. By centralizing all issuance paths through a single SANCertManager, the proxy ensures consistent application of allowlists and rate-limiting across synchronous handshakes and asynchronous background tasks.
Sources: internal/server/san_cert_manager.go:43-60, internal/server/san_cert_issuance.go:13-17
System Architecture
The certificate management system is comprised of several interacting components coordinated by the SANCertManager and the DynamicDomainManager.
Core Components
| Component | Description |
|---|---|
| SANCertManager | The central authority managing ACME identities, certificate storage, and the domain allowlist. |
| DynamicDomainManager | Orchestrates pollers for dynamic domain sources and triggers background issuance/renewal. |
| CertRenewer | A background loop that checks managed certificates and initiates renewal based on ARI or expiration thresholds. |
| TokenBucket | A rate-limiting mechanism that caps the number of ACME orders across all issuance paths. |
| DomainGrouper | Logic that analyzes batches of domains to decide if they should be collapsed into wildcards. |
Sources: internal/server/san_cert_manager.go:64-118, internal/server/dynamic_domains.go:53-73, internal/server/domain_renewal.go:68-78
Management Logic Flow
The following diagram illustrates how the SANCertManager handles a request for a certificate during a TLS handshake.
Issuance Strategies
The proxy supports two primary challenge types: HTTP-01 and DNS-01. The choice of strategy is governed by the domain's configuration and available DNS providers.
Challenge Selection and Fallback
The obtainCertificate function implements the routing logic for ACME orders:
- DNS-01 Priority: If a DNS provider is configured for the domain's zone, the proxy attempts DNS-01 issuance. This is required for wildcard certificates.
- HTTP-01 Fallback: If DNS-01 fails and
--acme-http-fallbackis enabled, the proxy retries the order using HTTP-01 (for non-wildcard domains). - HTTP-01 Default: Domains not covered by a DNS provider mapping default to HTTP-01 validation.
Sources: internal/server/san_cert_issuance.go:23-53
SAN Batching and Wildcards
To optimize resource usage, the system batches pending domains into single certificates:
- Max SANs: Limits batches to 100 domains per certificate.
- Domain Grouping: Subdomains can be collapsed into single-level wildcards (e.g.,
*.example.com) if a DNS provider is available andPreferWildcardis set. - Zone Splitting: Orders are automatically partitioned by DNS provider zone to ensure one ACME order never spans multiple providers.
Sources: internal/server/san_cert_manager.go:40-42, internal/server/san_cert_issuance.go:81-105, internal/server/san_cert_manager.go:374-399
Renewal Process
The certRenewer component maintains the validity of all issued certificates through a periodic background reconcile loop (defaulting to once per hour).
Renewal Triggers
Renewal timing is determined by two methods:
- ACME Renewal Information (ARI): If the ACME server supports RFC 9773, the proxy uses the suggested renewal window provided by the CA.
- Fallback Window: If ARI is unavailable, the proxy calculates a threshold at two-thirds of the certificate's lifetime, adding stable jitter to prevent fleet-wide synchronization.
Sources: internal/server/domain_renewal.go:136-160
Reconciliation Flow
Configuration and State
Certificate management requires persistent storage to maintain ACME account keys and the mapping between domains and certificates.
Configuration Options
| Flag | Type | Description |
|---|---|---|
Email | String | ACME account email for registration. |
Directory | String | ACME directory URL (Defaults to Let's Encrypt Production). |
DNSProvider | String | Default DNS-01 challenge provider. |
PreferWildcard | Boolean | Collapse subdomains into wildcards when possible. |
HTTPFallback | Boolean | Retry DNS failures via HTTP-01. |
Sources: internal/server/san_cert_manager.go:44-62
Persistence Details
- CachePath: Stores
acme_user.json(account key) and individual certificate/key PEM files organized by a uniquesan:ID. - StatePath: Stores
acme.state, a JSON snapshot of theManagedCertregistry and domain-to-certificate mappings. - Atomic Writes: The proxy uses a temporary file and rename strategy (
.tmpto final path) to ensure state files are never corrupted during concurrent writes.
Sources: internal/server/san_cert_manager.go:520-565, internal/server/san_cert_manager.go:585-613
Dynamic Domain Support
The DynamicDomainManager handles certificates for domains that are not known at deploy time but are fetched from external sources (e.g., an application API).
- Asynchronous Issuance: Unlike deploy-registered hosts that can trigger synchronous provisioning during a handshake, dynamic domains are always issued in the background to avoid blocking TLS negotiation.
- Pre-flight Probes: Before attempting an ACME order for a dynamic domain, the proxy performs a self-probe to verify that the domain correctly routes back to the proxy. This prevents burning ACME rate limits on misconfigured or unreachable domains.
- Quarantine: Domains that fail issuance are placed in a quarantine with exponential backoff to avoid repeated failures.
Sources: internal/server/dynamic_domains.go:8-23, internal/server/san_cert_dynamic.go:10-14, internal/server/dynamic_domains.go:346-370
The automated issuance and renewal system ensures that Kamal-proxy remains a zero-maintenance edge router for TLS. By combining aggressive batching, wildcard optimization, and robust background renewal, the system provides reliable encryption for both static infrastructures and dynamic multi-tenant platforms.