State & Configuration Persistence
Relevant source files
The following files were used as context for generating this wiki page:
State and configuration persistence in kamal-proxy ensures that the proxy can recover its operational context—including service definitions, TLS certificates, and dynamic domain mappings—across restarts or deployments. The system relies on atomic file writes to JSON-formatted state files, typically stored within a dedicated data directory. This persistence mechanism is critical for maintaining high availability, as it allows the proxy to serve traffic immediately upon booting without waiting for external application health checks or ACME certificate orders.
The persistence architecture is decentralized across several managers (e.g., Router, SANCertManager, DynamicDomainManager), each responsible for its own domain-specific state. This ensures that a failure or corruption in one state file does not necessarily impact the entire system.
Router & Service Persistence
The Router is the primary orchestrator for service deployments. It persists the list of active services, their targets, and their specific configurations (like timeouts, buffer settings, and basic auth credentials) to a central state file.
Persistence Logic
The Router uses an "atomic rename" pattern to prevent file corruption. It writes to a .tmp file first and then renames it to the final .state path. Additionally, it maintains a .bak file that is updated after a successful clean decode of the primary state file.
Key Components
| Component | Description |
|---|---|
state.json | The primary persistence file for service configurations. |
state.json.bak | A backup file created after a successful restore to guard against corruption. |
RestoreLastSavedState | Function called during boot to reload services from disk. |
EnableTargetRecheckOnRestore | Optional setting to force health checks on restored targets before routing traffic. |
Sources: internal/server/router_test.go:453-488, internal/server/router_test.go:565-572
Certificate & ACME Persistence
The SANCertManager handles the persistence of ACME account data and TLS certificates. This ensures that the proxy does not hit ACME rate limits by re-ordering certificates on every restart.
Data Structures
The manager persists a managerState object containing mappings between domains and their corresponding managed certificates.
type managerState struct {
Certificates map[string]*ManagedCert `json:"certificates"`
DomainMap map[string]string `json:"domain_map"`
SavedAt time.Time `json:"saved_at"`
}
Sources: internal/server/san_cert_manager.go:618-622
Persistence Workflow
- ACME User: Stored in
acme_user.jsonwithin the cache path. It includes the email and the encoded private key. - Certificates: Each certificate is stored in a sub-directory named after a collision-resistant
certID. This directory containscert.pemandkey.pem. - Manager State: The
acme.statefile maps domains to thesecertIDs.
Sources: internal/server/san_cert_manager.go:564-610, internal/server/san_cert_manager.go:664-672
Dynamic Domain & Redirect Persistence
For environments where domains are discovered at runtime (e.g., via TLSDomainsSource), the DynamicDomainManager and DynamicRedirectManager maintain their own state files to ensure certificates and redirects are available immediately at boot.
Dynamic Domain State
The dynamic-domains.state file stores the last-known domain sets for each service, along with ETag information for polling and quarantine records for failed ACME attempts.
Persistence Summary Table
| Manager | State File | Data Persisted |
|---|---|---|
Router | kamal-proxy.state | Service options, targets, health check configs, pause states. |
SANCertManager | acme.state | Domain-to-Certificate ID mappings, certificate metadata. |
DynamicDomainManager | dynamic-domains.state | Polled domain lists, ETags, ACME quarantine records. |
DynamicRedirectManager | redirects.state | Dynamic redirect maps and source ETags. |
Sources: internal/server/dynamic_domain_state.go:24-40, internal/server/dynamic_redirects.go:27-31, ROADMAP.md:144-150
Implementation Constraints
Persistence logic in kamal-proxy follows specific safety rules to ensure reliability:
- Atomic Writes: All state updates use a temporary file followed by an atomic
os.Renameto prevent partial writes during crashes. - Concurrency Control: A
stateMu(orsaveLock) is used to serialize writes from different goroutines (e.g., issuer vs. renewer) to the same file. - Environment Overrides: The root configuration directory and socket paths can be overridden via
AlternateConfigDiror environment variables likeKAMAL_PROXY_SOCKET. - Safe Defaults: When restoring from old state files, new features (like
BasicAuthorCompressionOptions) must default to "disabled" to ensure backward compatibility.
Sources: internal/server/san_cert_manager.go:648-662, internal/server/config_test.go:28-32, internal/server/dynamic_redirects.go:51-54, internal/server/dynamic_domain_state.go:76-88
Persistence in kamal-proxy is a multi-layered system designed to minimize external dependencies during startup. By caching configuration and cryptographic material locally, the proxy achieves resilience against upstream application failures and ACME provider instability.