basecamp/kamal · error · Kamal::Cli::LockError
Timed out waiting for deploy lock
Error message
Timed out waiting for deploy lock
What it means
When a locking Kamal command finds the deploy lock held by another automatic deploy and wait_when_locked is set, it retries acquisition until a deadline (now + timeout seconds), printing 'Retrying in Xs (Ys remaining)'. If the deadline passes while the lock is still held, Kamal prints 'Timed out after Ns waiting for the deploy lock' and raises Kamal::Cli::LockError with 'Timed out waiting for deploy lock'.
Source
Thrown at lib/kamal/cli/base.rb:178
break
rescue LockHeldError
unless details_shown
status = capture_lock_status
say "Deploy lock is held by:", :magenta
puts status
unless status.include?(AUTOMATIC_DEPLOY_LOCK_MESSAGE)
raise LockError, "Deploy lock held manually, not waiting. Run 'kamal lock help' for more information"
end
details_shown = true
end
remaining = (deadline - Time.now).to_i
if remaining <= 0
say "Timed out after #{timeout}s waiting for the deploy lock", :red
raise LockError, "Timed out waiting for deploy lock"
end
say "Retrying in #{interval}s (#{remaining}s remaining)...", :magenta
sleep [ interval, remaining ].min
end
end
def release_lock
say "Releasing the deploy lock...", :magenta
execute_lock_release
KAMAL.holding_lock = false
end
def raise_if_locked
yield
rescue LockHeldError
say "Deploy lock already in place!", :redView on GitHub (pinned to eee0083b38)
Solutions
- Check lock status: `kamal lock status` — see which deploy holds it and whether it is still genuinely running.
- If the holder is dead/stale, release with `kamal lock release` (or wait for the active deploy to finish and simply re-run).
- Increase the wait timeout in config/deploy.yml under lock: wait_when_locked: timeout: 300 (and interval) if concurrent deploys are normal for you.
- Serialize deploys in CI (mutex/job concurrency limits) so they queue instead of racing.
Example fix
# before (config/deploy.yml)
lock:
wait_when_locked:
timeout: 30
# after: wait long enough for overlapping deploys to finish
lock:
wait_when_locked:
interval: 5
timeout: 600 Defensive patterns
Strategy: retry
Validate before calling
loop do
status = `kamal lock status 2>/dev/null`
break if status.empty? || !status.include?("Locked") # adapt to output
abort "deploy lock busy for too long" if Time.now > @deadline
sleep 10
end
system("kamal deploy") Try / catch
retries = 0
begin
Kamal::CLI::Deploy.new.perform
rescue Kamal::Cli::LockError => e
raise unless e.message.include?("Timed out") && (retries += 1) <= 3
sleep 60 # let the other deploy finish, then retry
retry
end Prevention
- Set lock: wait_when_locked timeout to comfortably exceed your longest deploy.
- Serialize deploys in CI (concurrency groups) so waits are rare and short.
- After any cancelled deploy, verify `kamal lock status` is clean before the next scheduled run.
When it happens
Trigger: Two concurrent deploys (e.g. CI job plus a manual deploy): the second waits; if the first deploy runs longer than the configured wait timeout (wait_when_locked.timeout, default 30s in the lock config), the waiting deploy raises this error. Also triggered by a stale automatic lock left behind by a crashed deploy, since the status does contain the automatic message so Kamal keeps waiting until the deadline.
Common situations: Overlapping CI pipelines deploying the same environment; a long-running first deploy (slow image build/pull) exceeding the waiter's timeout; a previous deploy killed mid-run leaving the lock directory on the primary host.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Deploy lock held manually, not waiting. Run 'kamal lock help
- Deploy lock found. Run 'kamal lock help' for more informatio
- Raw is not compatible with interactive
- Detach is not compatible with #{incompatible_options.join("
- Raw is not compatible with #{incompatible_options.join(" or
AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21).
Data as JSON: /api/errors/31901a45ad3a6c79.
Report an issue: GitHub.