Skip to content

Add idle service configuration for kamal-proxy - #1916

Open
komagata wants to merge 2 commits into
basecamp:mainfrom
komagata:komagata/kamal-configuration
Open

Add idle service configuration for kamal-proxy#1916
komagata wants to merge 2 commits into
basecamp:mainfrom
komagata:komagata/kamal-configuration

Conversation

@komagata

@komagata komagata commented Jul 20, 2026

Copy link
Copy Markdown

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

proxy:
  idle:
    timeout: 300
    wake_timeout: 30

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. With retain_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 --json for 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 --roles restricts 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

  • Prune command/CLI tests: 22 runs, 66 assertions, no failures/errors. Includes a shell-pipeline regression test for preserving an older current target while removing obsolete history, worker-only pruning, readers/rollout targets, malformed responses, and lookup failures.
  • Command/CLI/configuration tests, including the previously blocked Docker-dependent builder files: 660 runs, 2457 assertions, no failures/errors.
  • Docker Engine 29.7.2: ran the generated prune/inspect commands against isolated, real stopped containers. Without protection the current container was deleted; with protection it survived newer sibling-role history, a newer same-role failed-release fixture, and the default retention of five. Obsolete history was removed and the protected container could be restarted. Proxy-list JSON was supplied as a fixture, so this verifies Docker pruning and ID resolution, not the complete HTTP sleep/wake path.
  • RuboCop on changed Ruby files and git diff --check pass.

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 69c91aba with kamal-proxy commit 25977def using 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 deploy or a real failed application rollout. Test containers/network were removed afterward.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_timeout configuration 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.

Comment thread lib/kamal/configuration.rb Outdated
Copilot AI review requested due to automatic review settings July 21, 2026 13:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread test/configuration/proxy_test.rb Outdated
Comment thread test/configuration/proxy_test.rb Outdated
Copilot AI review requested due to automatic review settings July 21, 2026 13:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

@komagata

komagata commented Sep 1, 2026

Copy link
Copy Markdown
Author

Hi! The corresponding kamal-proxy implementation in basecamp/kamal-proxy#228 has now been rebased onto the latest main. go test ./..., the race tests, and go vet ./... are all passing.

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!

@komagata

komagata commented Sep 1, 2026

Copy link
Copy Markdown
Author

@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!

@komagata
komagata force-pushed the komagata/kamal-configuration branch from cd12730 to 0fa916c Compare September 7, 2026 20:27
@komagata
komagata force-pushed the komagata/kamal-configuration branch from 0fa916c to 69c91ab Compare September 8, 2026 07:23
@komagata

komagata commented Sep 9, 2026

Copy link
Copy Markdown
Author

@djmb, could you please approve the pending CI run for this PR when you have a chance? The latest run has action_required and no jobs have started: https://github.com/basecamp/kamal/actions/runs/34199063806

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants