basecamp/kamal · error · RuntimeError

gcloud CLI is not installed

Error message

gcloud CLI is not installed

What it means

Raised by check_dependencies! in Kamal::Secrets::Adapters::GcpSecretManager when `gcloud --version 2> /dev/null` exits non-zero. Base#fetch runs this probe before login and fetching, so any adapter use fails fast when the Google Cloud CLI binary is missing or unusable on PATH.

Solutions

  1. Install the Google Cloud CLI (e.g. `brew install --cask google-cloud-sdk` or the official install script) and verify `gcloud --version` in the exact environment running kamal.
  2. Add the SDK bin dir to PATH (Dockerfile ENV PATH=..., CI export) where installing under $HOME.
  3. Pin the SDK install/upgrade in provisioning so deploy hosts always have it.
Defensive patterns

Strategy: validation

Validate before calling

require "open3"

def gcloud_installed?
  Open3.capture3("gcloud", "--version")[2].success?
end

abort "Install the Google Cloud CLI (gcloud) before pulling GCP secrets" unless gcloud_installed?

Prevention

When it happens

Trigger: adapter.fetch(...) (e.g. `kamal secrets pull` with adapter gcp_secret_manager) on a host without the gcloud binary on PATH: google-cloud-sdk never installed, installed via a version manager not active in the kamal process, or a broken/incompatible SDK install.

Common situations: Minimal Docker/CI images (gcloud is not bundled); installing via gcloud's install dir (~/$HOME/google-cloud-sdk/bin) without adding it to PATH in non-login shells; macOS where the SDK lives in ~/google-cloud-sdk but CI uses a clean shell.

Related errors


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

Appendix: source

Thrown at lib/kamal/secrets/adapters/gcp_secret_manager.rb:93

          items[secret] = [ project, secret_name, secret_version ]
        end
      end
    end

    def run_command(command, project: "default", user: "default", service_account: nil)
      full_command = [ "gcloud", command ]
      full_command << "--project=#{project.shellescape}" unless project == "default"
      full_command << "--account=#{user.shellescape}" unless user == "default"
      full_command << "--impersonate-service-account=#{service_account.shellescape}" if service_account
      full_command << "--format=json"
      full_command = full_command.join(" ")

      result = `#{full_command}`.strip
      JSON.parse(result)
    end

    def check_dependencies!
      raise RuntimeError, "gcloud CLI is not installed" unless cli_installed?
    end

    def cli_installed?
      `gcloud --version 2> /dev/null`
      $?.success?
    end

    def logged_in?
      JSON.parse(`gcloud auth list --format=json`).any?
    end

    def parse_account(account)
      account.split("|", 2)
    end

    def is_user?(candidate)
      candidate.include?("@")
    end

View on GitHub (pinned to eee0083b38)