-
Notifications
You must be signed in to change notification settings - Fork 692
Add idle container configuration for kamal-proxy #1800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,6 +21,10 @@ def app_port | |||||||
| proxy_config.fetch("app_port", 80) | ||||||||
| end | ||||||||
|
|
||||||||
| def idle? | ||||||||
| proxy_config.dig("idle", "timeout").present? | ||||||||
|
||||||||
| proxy_config.dig("idle", "timeout").present? | |
| idle_config = proxy_config["idle"] | |
| idle_config.is_a?(Hash) && (idle_config["timeout"].present? || idle_config["wake_timeout"].present?) |
Copilot
AI
Mar 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deploy_options will include idle-wake-timeout whenever idle.wake_timeout is set, even if idle.timeout is missing/disabled. That can produce a deploy command with a wake-timeout flag but no idle-timeout, and (depending on how the proxy container is started) without Docker socket access. Consider only emitting idle-wake-timeout when idle.timeout is configured, or enforce the dependency in validation.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,10 @@ def debug? | |
| run_config.fetch("debug", nil) | ||
| end | ||
|
|
||
| def docker_socket? | ||
| run_config.fetch("docker_socket", config.any_role_use_proxy_idle?) | ||
| end | ||
|
Comment on lines
+20
to
+22
|
||
|
|
||
| def publish? | ||
| run_config.fetch("publish", true) | ||
| end | ||
|
|
@@ -94,6 +98,7 @@ def run_command_options | |
| def docker_options_args | ||
| [ | ||
| *apps_volume_args, | ||
| *("--volume=/var/run/docker.sock:/var/run/docker.sock" if docker_socket?), | ||
| *publish_args, | ||
| *logging_args, | ||
| *("--expose=#{metrics_port}" if metrics_port.present?), | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,6 +21,16 @@ def validate! | |||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if config["idle"].present? | ||||||||||||||||||||||||||||||||||||||||||
| if config["idle"]["timeout"].present? && !config["idle"]["timeout"].is_a?(Integer) | ||||||||||||||||||||||||||||||||||||||||||
| error "Idle timeout must be an integer (seconds)" | ||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if config["idle"]["wake_timeout"].present? && !config["idle"]["wake_timeout"].is_a?(Integer) | ||||||||||||||||||||||||||||||||||||||||||
| error "Idle wake timeout must be an integer (seconds)" | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+25
to
+30
|
||||||||||||||||||||||||||||||||||||||||||
| if config["idle"]["timeout"].present? && !config["idle"]["timeout"].is_a?(Integer) | |
| error "Idle timeout must be an integer (seconds)" | |
| end | |
| if config["idle"]["wake_timeout"].present? && !config["idle"]["wake_timeout"].is_a?(Integer) | |
| error "Idle wake timeout must be an integer (seconds)" | |
| if config["idle"]["timeout"].present? | |
| if !config["idle"]["timeout"].is_a?(Integer) | |
| error "Idle timeout must be an integer (seconds)" | |
| elsif config["idle"]["timeout"] <= 0 | |
| error "Idle timeout must be a positive integer (seconds)" | |
| end | |
| end | |
| if config["idle"]["wake_timeout"].present? | |
| if !config["idle"]["wake_timeout"].is_a?(Integer) | |
| error "Idle wake timeout must be an integer (seconds)" | |
| elsif config["idle"]["wake_timeout"] <= 0 | |
| error "Idle wake timeout must be a positive integer (seconds)" | |
| end |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -221,6 +221,20 @@ class CommandsProxyTest < ActiveSupport::TestCase | |||||||||||||||||
| new_command.run.join(" ") | ||||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
| test "docker socket mount when idle configured" do | ||||||||||||||||||
| @config[:proxy] = { "idle" => { "timeout" => 300 }, "run" => { "version" => "v0.9.2" } } | ||||||||||||||||||
| assert_equal \ | ||||||||||||||||||
| "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $PWD/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config --volume=/var/run/docker.sock:/var/run/docker.sock --publish 80:80 --publish 443:443 --log-opt max-size=10m basecamp/kamal-proxy:v0.9.2 kamal-proxy run", | ||||||||||||||||||
| new_command.run.join(" ") | ||||||||||||||||||
| end | ||||||||||||||||||
|
|
||||||||||||||||||
|
||||||||||||||||||
| test "docker socket mount when idle configured without run config" do | |
| @config[:proxy] = { "idle" => { "timeout" => 300 } } | |
| assert_equal \ | |
| "docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $PWD/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config --volume=/var/run/docker.sock:/var/run/docker.sock --publish 80:80 --publish 443:443 --log-opt max-size=10m basecamp/kamal-proxy:v0.9.2 kamal-proxy run", | |
| new_command.run.join(" ") | |
| end |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -105,6 +105,32 @@ class ConfigurationProxyTest < ActiveSupport::TestCase | |||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| test "idle configuration" do | ||||||||||||||||||||||||||||||||||||||||||
| @deploy[:proxy] = { | ||||||||||||||||||||||||||||||||||||||||||
| "host" => "example.com", | ||||||||||||||||||||||||||||||||||||||||||
| "idle" => { | ||||||||||||||||||||||||||||||||||||||||||
| "timeout" => 300, | ||||||||||||||||||||||||||||||||||||||||||
| "wake_timeout" => 30 | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| proxy = config.proxy | ||||||||||||||||||||||||||||||||||||||||||
| assert_equal true, proxy.idle? | ||||||||||||||||||||||||||||||||||||||||||
| assert_equal "300s", proxy.deploy_options[:"idle-timeout"] | ||||||||||||||||||||||||||||||||||||||||||
| assert_equal "30s", proxy.deploy_options[:"idle-wake-timeout"] | ||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| test "idle validation" do | ||||||||||||||||||||||||||||||||||||||||||
| @deploy[:proxy] = { | ||||||||||||||||||||||||||||||||||||||||||
| "host" => "example.com", | ||||||||||||||||||||||||||||||||||||||||||
| "idle" => { | ||||||||||||||||||||||||||||||||||||||||||
| "timeout" => "300" | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| assert_raises(Kamal::ConfigurationError) { config.proxy } | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| assert_raises(Kamal::ConfigurationError) { config.proxy } | |
| assert_raises(Kamal::ConfigurationError) { config.proxy } | |
| @deploy[:proxy] = { | |
| "host" => "example.com", | |
| "idle" => { | |
| "timeout" => 0 | |
| } | |
| } | |
| assert_raises(Kamal::ConfigurationError) { config.proxy } | |
| @deploy[:proxy] = { | |
| "host" => "example.com", | |
| "idle" => { | |
| "timeout" => -1 | |
| } | |
| } | |
| assert_raises(Kamal::ConfigurationError) { config.proxy } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment for
run.docker_socketsays the default is "false, or true if idle is used", but the current implementation only defaults totruewhen the proxy is started via aproxy.runconfig (i.e., whenProxy::Runis used). If the proxy is started via the boot-file path (noproxy.run),proxy.idlecurrently won’t imply a Docker socket mount. Please either adjust the docs to reflect this limitation or update the boot-mode run options to match the documented default.