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 = nil

View on GitHub (pinned to eee0083b38)

Solutions

  1. Inspect the holder: `kamal lock status` shows the message and version recorded when the lock was taken.
  2. If no deploy is actually running, release it: `kamal lock release`, then retry your command.
  3. If a deploy is legitimately in progress, wait for it to finish instead of force-releasing.
  4. 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

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


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