Add idle service configuration for kamal-proxy - #1916
Conversation
There was a problem hiding this comment.
Pull request overview
Adds opt-in configuration for kamal-proxy “idle services” (scale-to-zero) in Kamal, including passing idle timeouts to kamal-proxy and enabling Docker socket access only when idle mode is configured.
Changes:
- Introduces
proxy.idle.timeout/proxy.idle.wake_timeoutconfiguration and validation. - Extends generated proxy deploy/run commands to include idle flags and (when enabled) mount/configure the Docker socket for lifecycle operations.
- Updates documentation and adds tests covering the new configuration and command output.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
lib/kamal/configuration.rb |
Adds a helper to detect whether proxy idle mode is enabled anywhere in the config. |
lib/kamal/configuration/proxy.rb |
Adds idle? and wires idle options into proxy deploy flags; ensures proxy run config exists when idle is enabled. |
lib/kamal/configuration/validator/proxy.rb |
Validates idle timeout / wake timeout constraints. |
lib/kamal/configuration/proxy/run.rb |
Adds docker socket flag + mount/group wiring into proxy container run args when idle is enabled. |
lib/kamal/configuration/docs/proxy.yml |
Documents proxy.idle and proxy.run.docker_socket. |
test/configuration/proxy_test.rb |
Adds tests for parsing/validating idle configuration and deploy option output. |
test/configuration/proxy/run_test.rb |
Adds tests for selecting default/custom docker socket paths under idle mode. |
test/commands/proxy_test.rb |
Verifies proxy docker run command includes socket mount/group-add and socket arg only when idle is enabled. |
test/commands/app_test.rb |
Verifies deploy command includes idle flags when configured. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Hi! The corresponding kamal-proxy implementation in basecamp/kamal-proxy#228 has now been rebased onto the latest main. Since these two PRs need to be considered together, I’d appreciate guidance on whether opt-in scale-to-zero support fits the direction of Kamal and kamal-proxy. If the direction is acceptable, I’m happy to adjust the implementation or split the changes into smaller PRs to make review easier. Thanks! |
|
@djmb, would you be able to advise whether this direction fits Kamal? The corresponding kamal-proxy implementation is in basecamp/kamal-proxy#228 and is up to date with main. Thank you! |
cd12730 to
0fa916c
Compare
0fa916c to
69c91ab
Compare
|
@djmb, could you please approve the pending CI run for this PR when you have a chance? The latest run has The branch now has two commits: idle configuration/socket access, and protection of registered proxy targets during pruning. Local command/CLI/configuration tests passed (660 tests), including the Docker-dependent tests. Getting upstream CI running would help verify the supported Ruby matrix before review. Thank you! |
Summary
Add opt-in idle service configuration for basecamp/kamal-proxy#228, including protection against pruning containers that are sleeping but still serving as registered proxy targets.
This continues the work started by @martijnenco in #1800. Thank you for providing the original configuration approach.
Behavior
When a role or accessory enables idle mode, Kamal passes idle timeouts to the proxy and mounts the configured Docker socket with its numeric host group ID. Without idle configuration, the existing run/deploy commands and pruning behavior are unchanged.
Direct Docker access is opt-in and grants host-level container control, following the approach agreed in basecamp/kamal-proxy#222.
Protect sleeping containers from pruning
A sleeping current container is
exited, just like obsolete deployment history. Withretain_containers: 1, a newer stopped sibling-role container or a failed new deployment can otherwise cause pruning to remove the still-current target.While idle mode is configured, pruning now reads
kamal-proxy list --jsonfor the host's configured proxied app roles, resolves active/read/rollout targets to full Docker container IDs, and excludes those IDs before applying the retention limit. It also protects sibling roles when--rolesrestricts the invocation. The existing service/destination deploy lock covers lookup and deletion.If listing, validation, or container inspection fails, the host's container prune is skipped with a warning. Hosts without proxied app roles retain normal pruning behavior. Accessory containers use separate service labels and are not included in app container pruning.
Deploy configuration changes that disable idle mode before pruning with the new configuration. Direct Docker pruning and concurrent direct proxy CLI changes are outside Kamal's lock/protection.
Validation
git diff --checkpass.Earlier idle run/deploy behavior was validated with Docker and a Rails app on a 2 GB Ubuntu VPS; that validation predates this prune protection. Earlier results: https://docs.komagata.org/6456
HTTP sleep/wake and prune verification (2026-09-10)
Verified Kamal commit
69c91abawith kamal-proxy commit25977defusing an isolated Docker network and the proxy's example HTTP upstream. The test used actual proxy RPC/JSON output, Kamal-generated deploy arguments, and Kamal's target parsing, Docker inspection, and prune command generation.Both newer sibling-role history and a newer same-role stopped-container fixture passed: HTTP 200 → automatic idle stop → prune preserves the sleeping current target and removes obsolete history → HTTP wakes that same container and returns 200 → automatic sleep again. Prune itself did not wake the application. This was a local Docker component integration test, not a full SSH-based
kamal deployor a real failed application rollout. Test containers/network were removed afterward.