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!", :red

View on GitHub (pinned to eee0083b38)

Solutions

  1. Check lock status: `kamal lock status` — see which deploy holds it and whether it is still genuinely running.
  2. If the holder is dead/stale, release with `kamal lock release` (or wait for the active deploy to finish and simply re-run).
  3. Increase the wait timeout in config/deploy.yml under lock: wait_when_locked: timeout: 300 (and interval) if concurrent deploys are normal for you.
  4. 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

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

Related errors


AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21). Data as JSON: /api/errors/31901a45ad3a6c79. Report an issue: GitHub.