basecamp/kamal · error · Kamal::Cli::LockError
Deploy lock found. Run 'kamal lock help' for more informatio
Error message
Deploy lock found. Run 'kamal lock help' for more information
What it means
raise_if_locked wraps commands that must not proceed when a deploy lock exists (used when wait_when_locked is not configured, or for non-waiting commands). Acquiring the lock on the primary host fails with LockHeldError when the lock directory already exists (`cannot create directory`); Kamal prints 'Deploy lock already in place!' plus the captured status (holder message and version), then raises Kamal::Cli::LockError pointing at `kamal lock help`.
Source
Thrown at lib/kamal/cli/base.rb:198
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!", :red
puts capture_lock_status
raise LockError, "Deploy lock found. Run 'kamal lock help' for more information"
end
def execute_lock_acquire(message)
on(KAMAL.primary_host) { execute *KAMAL.lock.acquire(message, KAMAL.config.version), verbosity: :debug }
rescue SSHKit::Runner::ExecuteError => e
raise LockHeldError if e.message =~ /cannot create directory/
raise
end
def execute_lock_release
on(KAMAL.primary_host) { execute *KAMAL.lock.release, verbosity: :debug }
rescue SSHKit::Runner::ExecuteError => e
raise LockMissingError if e.message =~ /No such file or directory/
raise
end
def capture_lock_status
status = nilView on GitHub (pinned to eee0083b38)
Solutions
- Inspect the holder: `kamal lock status` shows the message and version recorded when the lock was taken.
- If no deploy is actually running, release it: `kamal lock release`, then retry your command.
- If a deploy is legitimately in progress, wait for it to finish instead of force-releasing.
- Add lock: wait_when_locked in config/deploy.yml so deploys queue rather than abort when overlapped.
Example fix
# before
kamal deploy # -> Deploy lock found. Run 'kamal lock help' ...
# after
kamal lock status
kamal lock release # only if no deploy is really running
kamal deploy
# or make deploys queue instead of failing (config/deploy.yml):
lock:
wait_when_locked:
interval: 5
timeout: 300 Defensive patterns
Strategy: try-catch
Validate before calling
# Pre-check before any locking command:
system("kamal lock status >/dev/null 2>&1") # non-zero/locked output => inspect before deploy
# Or in pipelines: gate on a clean status file you maintain after each release. Type guard
def lock_free?(status_output)
status_output.strip.empty? || status_output.include?("No lock")
end Try / catch
begin
Kamal::CLI::Deploy.new.perform
rescue Kamal::Cli::LockError => e
status = `kamal lock status`
warn "lock held:\n#{status}"
# release only after confirming no active deploy: kamal lock release
raise
end Prevention
- Configure wait_when_locked so overlapping deploys queue instead of erroring.
- Make CI releases idempotent and ensure a release step runs `kamal lock release` on failure paths.
- Never remove the lock directory by hand on the primary host; use kamal lock release.
When it happens
Trigger: Running any locking Kamal command (deploy, app stop/restart, proxy reboot, etc.) while the lock directory exists on the primary host — from a crashed previous deploy, a concurrent deploy, or a manual `kamal lock acquire`. Typical when no wait_when_locked is configured, so there is no waiting loop.
Common situations: CI deploy was cancelled mid-run leaving the lock; two developers deploying simultaneously; deploy process killed by OOM/timeout; previous deploy failed before its ensure-release ran.
Related errors
- Deploy lock held manually, not waiting. Run 'kamal lock help
- Timed out waiting for deploy lock
- 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/9e5bbfd33aa60155.
Report an issue: GitHub.