You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #19. Waking a slept service costs a full container start plus an application boot — basecamp#228 measured ~3.15s for a real Rails app on a 2 GB VPS. (Our implementation has not been measured; that figure is theirs.) The first visitor after an idle period pays all of it.
Why docker pause is not the answer on its own
The obvious idea is docker pause/unpause instead of stop/start: unpause is effectively instant, since it only thaws the cgroup freezer.
But pause does not reclaim any memory. The processes stay resident — docker stats still shows the full RSS. Reclaiming that memory is the entire point of #19: the motivating measurement is a Rails app holding 200–315 MiB while idle. A paused service frees nothing, so on its own this trades away the feature's only benefit for a faster wake.
What could actually work: tiered idling
Two thresholds rather than one:
After
Action
Wake cost
Memory reclaimed
short idle (say 5 min)
docker pause
~instant
none
long idle (say 1 h)
docker stop
full boot
all of it
That gives fast wakes across a lunch break and full reclaim overnight, which matches how review apps and internal tools are actually used. It fits the existing design: IdleController already has the state machine and ContainerLifecycle is the seam, so this is a third state plus a second timer, not a redesign.
Open questions:
Does a paused container still pass health checks? (It will not answer, so the pool handling for paused differs from sleeping — probably suspend on pause too.)
Is two thresholds too many knobs for the value? Possibly ship --sleep-after alone first and add the pause tier only if cold starts prove annoying in practice.
Other angles worth considering
Pre-warm on a cheaper signal. Wake on a TLS handshake or a DNS lookup rather than the HTTP request, buying a second or two before the request arrives.
Serve a holding page instead of blocking the connection, so the user sees something within 100ms. Changes the UX rather than the latency, and interacts with the --error-pages work already on dash.
Measure ours first. Before optimising, get a real number for this implementation on a real app. The plan in docs/plans/2026-07-29-scale-to-zero.md explicitly flags that we have no measurement of our own.
Follow-up to #19. Waking a slept service costs a full container start plus an application boot — basecamp#228 measured ~3.15s for a real Rails app on a 2 GB VPS. (Our implementation has not been measured; that figure is theirs.) The first visitor after an idle period pays all of it.
Why
docker pauseis not the answer on its ownThe obvious idea is
docker pause/unpauseinstead ofstop/start: unpause is effectively instant, since it only thaws the cgroup freezer.But pause does not reclaim any memory. The processes stay resident —
docker statsstill shows the full RSS. Reclaiming that memory is the entire point of #19: the motivating measurement is a Rails app holding 200–315 MiB while idle. A paused service frees nothing, so on its own this trades away the feature's only benefit for a faster wake.What could actually work: tiered idling
Two thresholds rather than one:
docker pausedocker stopThat gives fast wakes across a lunch break and full reclaim overnight, which matches how review apps and internal tools are actually used. It fits the existing design:
IdleControlleralready has the state machine andContainerLifecycleis the seam, so this is a third state plus a second timer, not a redesign.Open questions:
kamal deployprune? Prune filtersstatus=exited; paused ispaused, so it likely does not hit Prune removes containers kamal-proxy has put to sleep (scale-to-zero) kamal#62. Worth confirming — if true, the pause tier sidesteps that problem entirely.--sleep-afteralone first and add the pause tier only if cold starts prove annoying in practice.Other angles worth considering
--error-pageswork already ondash.docs/plans/2026-07-29-scale-to-zero.mdexplicitly flags that we have no measurement of our own.Blocked on #19 landing in full.
Refs #19, #58, zoolutions/kamal#62