# gcloud CLI Cheat Sheet — Environment & Configuration Management

> **Tool:** Google Cloud CLI (gcloud)
> **Category:** Cloud CLIs
> **Verified against:** Google Cloud SDK 553.0.0, verified via `gcloud topic configurations`, `gcloud info`,
> `gcloud config set --help`, `gcloud init --help`, `gcloud components --help`, `gcloud auth configure-docker
> --help` run locally, a real inspection of `~/.config/gcloud/` on a working install, and
> https://cloud.google.com/sdk/docs/properties (property precedence), 2026-09-17
> **Official docs:** https://cloud.google.com/sdk/docs and https://cloud.google.com/sdk/gcloud/reference/topic/configurations

## What it is and where it fits 🎯

Every other page in this tool covers a GCP service surface, Compute (`02`), Storage & Networking (`03`),
Databases (`04`), Serverless & Messaging (`05`), Logging, Monitoring & Governance (`06`), Load Balancing &
DNS (`07`), Secret Manager & KMS (`08`), Artifact Registry & Cloud Build (`09`), and Cloud Deploy, Workflows
& IAP (`10`). This page covers none of that. It covers the SDK itself: what actually lands on disk when you install `gcloud`, what
`gcloud init` does to your home directory, what a "named configuration" really is (gcloud's answer to an AWS
CLI named profile or a kubectl context), how properties resolve when a flag, an environment variable, and a
saved configuration all disagree, and how any of this changes once you're running inside a container instead
of on your own laptop. Authentication and IAM identity itself (service accounts, Workload Identity
Federation, role bindings) is covered on page `01` — this page is about the CLI's own local state, not about
who you're authenticated as.

Skipping this page is how most "works on my machine, breaks in CI" `gcloud` problems happen: a script that
implicitly depends on a laptop's `default` configuration having a project set, run on a CI runner with no
such configuration, fails in a way that looks like a permissions problem but is actually a missing property.

## Installation and version check

Full per-platform install commands (apt, Homebrew, the interactive installer) are on page `01` — this page
assumes `gcloud` is already on your `PATH`. Two commands worth knowing before anything else:

```bash
gcloud version               # confirm the CLI and every installed component's version
gcloud components update     # keep the CLI and all installed components in lockstep
```

> [!TIP]
> Run `gcloud components update` on a schedule (weekly, or at the top of a CI image build) rather than only
> when something breaks. GCP ships new `gcloud` releases roughly every two weeks, and a stale CLI is a
> real, recurring source of "that flag doesn't exist" reports that turn out to just be an old install.

## Core concepts: what "the SDK" actually is on disk

```mermaid
flowchart TD
    Home["CLOUDSDK_CONFIG root<br/>~/.config/gcloud (Linux/macOS)"] --> Active["active_config<br/>(just a filename)"]
    Home --> Configs["configurations/<br/>config_&lt;name&gt; per profile"]
    Home --> Creds["credentials.db +<br/>access_tokens.db"]
    Home --> ADC["application_default_credentials.json"]
    Home --> Legacy["legacy_credentials/<br/>per-account gcloud_id_token"]
    Home --> Logs["logs/<br/>one dir per invocation date"]
    Active -.->|"names which file<br/>in configurations/ is live"| Configs

    classDef info fill:#e5f0fa,stroke:#1d6fb8,color:#10161c
    classDef accent fill:#f0e9fb,stroke:#6d43c0,color:#10161c
    classDef crit fill:#fbe8e6,stroke:#b3261e,color:#10161c
    class Home info
    class Configs,Active accent
    class Creds,ADC,Legacy crit
```

The directory is the closest thing `gcloud` has to a "home." Nothing about your project, your active
account, or your default region lives anywhere else, it's all just files under this one tree. The red nodes
above (`credentials.db`, `application_default_credentials.json`, `legacy_credentials/`) are the ones that
hold real, usable secrets: anyone who can read them can authenticate as you or as whatever service account
you last activated, with no further password prompt.

> [!CAUTION]
> **Never commit `~/.config/gcloud/` (or any subset of it) into a repository, a Docker image layer, or a
> shared network drive.** `credentials.db` and `legacy_credentials/*/adc.json` are long-lived OAuth refresh
> tokens, functionally equivalent to a password with no expiry until explicitly revoked. This has caused
> real incidents: a team that baked a developer's `~/.config/gcloud` into a "convenience" base image for
> faster CI cold starts shipped that developer's personal credentials into every downstream image built
> from it, discovered only when an access review flagged logins from build agents that had never run
> `gcloud auth login`.

## Where gcloud lives — the home directory, per platform

| Platform | Default location | Override with |
|---|---|---|
| Linux / macOS | `~/.config/gcloud` | `CLOUDSDK_CONFIG=/some/path` |
| Windows | `%APPDATA%\gcloud` | `CLOUDSDK_CONFIG=C:\some\path` |

```bash
gcloud info --format="value(config.paths.global_config_dir)"   # the actual resolved path, whatever OS you're on
```

`CLOUDSDK_CONFIG` is the single environment variable that relocates the entire directory tree from the
diagram above, not just one file inside it. The two situations where you actually want to set it: `$HOME`
is read-only or shared (a hardened CI runner image, a locked-down corporate laptop profile), or you
deliberately want two fully isolated `gcloud` identities running side by side without touching named
configurations at all, one dedicated directory per identity, selected by which `CLOUDSDK_CONFIG` value a
given shell session exports.

## First-run setup: `gcloud init`

```bash
gcloud init                        # interactive: login, pick/create project, set default region + zone
gcloud init --console-only         # for a remote shell with no browser to launch
gcloud init --skip-diagnostics     # skip the network connectivity checks, faster on a known-good machine
```

`gcloud init` is a guided wrapper around three things you could otherwise do by hand: `gcloud auth login`,
`gcloud config set project`, and creating (or reusing) a named configuration to hold the result. It writes
exactly one file, `~/.config/gcloud/configurations/config_default`, the first time you run it, and marks
that configuration active by writing its name into `~/.config/gcloud/active_config`.

> [!TIP]
> For a scripted or headless first-time setup (a new CI runner image, a fresh dev-container), skip
> `gcloud init` entirely and go straight to the primitives it wraps: `gcloud auth login --no-launch-browser`
> (or `gcloud auth activate-service-account` for a non-interactive identity) followed by
> `gcloud config set project my-project-id`. `gcloud init`'s interactive prompts have nothing to offer a
> process with no human watching the terminal.

## Named configurations — gcloud's "profiles"

```bash
gcloud config configurations create staging       # a fresh, empty named configuration
gcloud config configurations activate staging     # switch which one is "active" for every future command
gcloud config configurations list                 # every configuration + which one is active + its account/project
gcloud config configurations describe staging      # full property dump for one configuration
gcloud config configurations rename staging stg     # rename without losing its properties
gcloud config configurations delete staging          # prompts for confirmation unless --quiet is passed
```

A configuration bundles account, project, default region/zone, and any other property you've set into one
named, switchable unit, the direct equivalent of an AWS CLI named profile (`~/.aws/config`'s `[profile
name]` blocks) or a kubectl context. Where it differs from both: **only one configuration is ever active
at a time for a given `CLOUDSDK_CONFIG` directory**, chosen by writing its name into a single
`active_config` file, not by an env var you have to remember to export in every shell (though `--configuration`
and `CLOUDSDK_ACTIVE_CONFIG_NAME` below give you that per-invocation override when you want it).

### Config file format

```ini
# ~/.config/gcloud/configurations/config_staging
[core]
account = ci-deployer@my-staging-project.iam.gserviceaccount.com
project = my-staging-project

[compute]
region = us-central1
zone = us-central1-a
```

Plain INI, one `[section]` per property group, `property = value` lines underneath, generated for you by
`gcloud config set` but perfectly safe to hand-write or template. This is the concrete detail that matters
for baking a config into a container image: `COPY` or template this one file into
`$CLOUDSDK_CONFIG/configurations/config_<name>` plus a one-line `active_config` file naming it, and the
image boots with that configuration already active, no interactive `gcloud init` required at container
start.

## Setting and reading individual properties

```bash
gcloud config set project my-project-id            # writes into the ACTIVE configuration's [core] section
gcloud config set compute/region us-central1
gcloud config set compute/zone us-central1-a
gcloud config get project                            # read one property back
gcloud config list                                    # every property set in the active configuration
gcloud config list --all                              # includes properties still at their unset default
gcloud config unset compute/zone
```

Properties are namespaced `section/name` (`core/project`, `compute/region`, `functions/region`), which
matters because it's exactly how the matching environment variable is spelled: uppercase the section and
name, join with an underscore, prefix `CLOUDSDK_`. `compute/region` becomes `CLOUDSDK_COMPUTE_REGION`,
`core/project` becomes `CLOUDSDK_CORE_PROJECT`. `gcloud config set project` (with no explicit section) is
shorthand for `core/project` specifically, since it's the property nearly every command needs.

## Precedence: flag beats env var beats saved configuration

```mermaid
flowchart LR
    Flag["1. Explicit flag<br/>--project=x"] --> Env["2. Environment variable<br/>CLOUDSDK_CORE_PROJECT=x"]
    Env --> Cfg["3. Active configuration's<br/>saved property"]
    Cfg --> Default["4. Built-in default<br/>(often none — command errors)"]

    classDef ok fill:#e5f5ea,stroke:#1f8a4c,color:#10161c
    classDef info fill:#e5f0fa,stroke:#1d6fb8,color:#10161c
    classDef muted fill:#eaeef1,stroke:#c3ccd4,color:#10161c
    class Flag ok
    class Env,Cfg info
    class Default muted
```

Highest wins. A one-off `gcloud compute instances list --project=other-project` overrides whatever the
active configuration says, without changing it; a `CLOUDSDK_CORE_PROJECT=other-project gcloud ...` prefix
does the same for exactly that one shell invocation, without touching any file on disk at all, the pattern
CI systems use so a pipeline never has to mutate a shared configuration to target a different project per
job. This is also how `--configuration=staging` (or `CLOUDSDK_ACTIVE_CONFIG_NAME=staging` as its env-var
equivalent) works: it's a global flag on `gcloud` itself, available to every command, that swaps which
saved configuration supplies tier-3 values for that one invocation, without running
`config configurations activate` and leaving a different configuration active afterward.

> [!IMPORTANT]
> **A named configuration's properties are local file state, not a live reflection of the project.**
> `gcloud init`'s own help text is explicit about this: changing a project's default Compute Engine zone in
> the Cloud Console does *not* change what your saved configuration reports as `compute/zone` — the two are
> independent, and a stale local zone/region setting is a genuinely common cause of "why did my instance
> just get created in the wrong region" when a team's cloud-side defaults have moved on since someone's
> laptop was last configured.

## Diagnosing your environment: `gcloud info`

```bash
gcloud info                                                        # active account, project, SDK root, Python version, everything
gcloud info --show-log                                             # append the most recent log file's contents
gcloud info --format="value(config.paths.active_config_path)"       # exact path of the currently active config file
gcloud info --format="value(config.account,config.project)"         # just the two values you usually actually want
```

`gcloud info` is the single command to run before reporting "gcloud is broken" to a teammate or filing a
support ticket, it dumps the resolved account, project, SDK installation path, active configuration path,
Python interpreter in use, and a handful of recent log file locations in one shot, which is almost always
enough to spot a stale configuration or a mismatched account without any further digging.

## Components: the SDK's own package manager

```bash
gcloud components list                    # every component gcloud knows about + installed/not-installed state
gcloud components install kubectl           # add an optional component (kubectl, gke-gcloud-auth-plugin, cloud-build-local, ...)
gcloud components install alpha beta        # unlock `gcloud alpha ...` / `gcloud beta ...` command surfaces
gcloud components remove COMPONENT_ID
gcloud components reinstall                 # rebuild the whole install if something's gone inconsistent
```

`core`, `gcloud-crc32c`, and a handful of others ship by default; `alpha`, `beta`, `bq`, `gsutil`, `kubectl`,
and `gke-gcloud-auth-plugin` are common opt-in installs. A command documented as `(BETA)` or `(ALPHA)` in its
own `--help` output genuinely will not run until the matching component is installed, that's not a
permissions error, it's a missing local component, and `gcloud components install beta` is the fix.

## Multiple accounts and switching between them

```bash
gcloud auth list                                     # every authenticated account on this machine + which is active
gcloud config set account jane@example.com            # switch the ACTIVE configuration's account (doesn't re-authenticate)
gcloud auth login other-account@example.com            # authenticate a second account without dropping the first
```

Full authentication mechanics, service accounts, impersonation, and Workload Identity Federation live on
page `01`. What belongs here: `gcloud auth list` and `gcloud config set account` operate on already-cached
credentials, switching *which* authenticated identity the active configuration points at is instant and
doesn't touch the network, whereas `gcloud auth login` is what actually round-trips through Google's OAuth
flow to add a new credential to the cache in the first place.

## Docker, CI, and a read-only or ephemeral `$HOME`

```dockerfile
# Dockerfile snippet — bake a service-account-driven config into a CI image
ENV CLOUDSDK_CONFIG=/etc/gcloud-config
COPY ci-config /etc/gcloud-config
RUN gcloud auth activate-service-account --key-file=/etc/gcloud-config/key.json
```

```bash
# Mount a config directory into a container at run time instead of baking it in
docker run --rm -v ~/.config/gcloud:/root/.config/gcloud gcr.io/my-project/my-tool:latest
```

```bash
gcloud auth configure-docker                          # register gcloud as Docker's credential helper for Google registries
gcloud auth configure-docker us-central1-docker.pkg.dev   # scope it to one Artifact Registry host instead of all Google registries
```

`gcloud auth configure-docker` writes into `~/.docker/config.json`, not into `~/.config/gcloud/` at all, it
tells `docker` itself to shell out to `gcloud` for registry credentials on every pull/push against a Google
registry host, which is what lets `docker push us-central1-docker.pkg.dev/...` work with no separate
`docker login` step.

> [!WARNING]
> **Mounting `~/.config/gcloud` read-write into an untrusted or multi-tenant container gives that container
> everything it needs to act as you, indefinitely, not just for the lifetime of the container.** A refresh
> token cached in `credentials.db` outlives the container. For anything short-lived or untrusted, prefer
> activating a scoped service account key (or better, Workload Identity Federation, page `01`) inside the
> container's own `CLOUDSDK_CONFIG` directory instead of sharing your personal one.

## Real-world scenario: one laptop, personal account and a CI-service-account identity, no cross-contamination

An engineer needs to run `gcloud` interactively as themselves for day-to-day debugging, and separately, run
it locally as the exact CI service account to reproduce a pipeline failure, without either identity
accidentally leaking into the other's session:

```bash
gcloud config configurations create personal
gcloud config set account jane@example.com --configuration=personal
gcloud config set project my-project-id --configuration=personal

gcloud config configurations create ci-repro
gcloud auth activate-service-account --key-file=/tmp/ci-key.json
gcloud config set account ci-deployer@my-project-id.iam.gserviceaccount.com --configuration=ci-repro
gcloud config set project my-project-id --configuration=ci-repro

gcloud config configurations activate personal      # back to interactive work
gcloud --configuration=ci-repro compute instances list   # one-off command as the CI identity, no need to switch active config
```

`--configuration=ci-repro` on that last line is the detail that makes this actually convenient day to day:
the engineer never has to remember to switch back to `personal` afterward, because that single command never
changed which configuration was active in the first place.

## Real-world scenario: a consultancy managing five clients' GCP projects from one machine

- [ ] Create one named configuration per client project: `gcloud config configurations create client-acme`
- [ ] Set `account`, `project`, and `compute/region` for each, matching that client's actual environment
- [ ] Alias each client to a shell function (`alias acme='gcloud config configurations activate client-acme'`)
      instead of typing the full configuration name every time
- [ ] Confirm `gcloud config configurations list` shows the expected account/project pairing for every
      client before running anything destructive, a wrong-client `terraform apply` or `gcloud ... delete`
      is the actual failure mode this whole setup exists to prevent
- [ ] Never share one configuration across two clients "temporarily", create a new one, it costs nothing

> [!TIP]
> `gcloud config configurations list`'s output includes the account and project columns specifically so this
> kind of pre-flight check is a single glance, not a `gcloud config list` round-trip per client.

## CI/CD integration recipe: isolated per-job config on a shared GitHub Actions runner

```yaml
# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      CLOUDSDK_CONFIG: ${{ runner.temp }}/gcloud-config   # isolated per job run, cleaned up with the runner
    steps:
      - uses: actions/checkout@v4
      - uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: projects/<project-number>/locations/global/workloadIdentityPools/github-pool/providers/github-provider
          service_account: ci@my-project-id.iam.gserviceaccount.com
      - uses: google-github-actions/setup-gcloud@v2
      - run: gcloud config list   # confirm the isolated config actually picked up the WIF-authenticated identity
```

Setting `CLOUDSDK_CONFIG` to a path under `runner.temp` (rather than letting it default to the runner's
`$HOME`) means a self-hosted runner reused across many jobs never accumulates state between them, and two
concurrent jobs on the same self-hosted machine can never see each other's active account or project.

## Common pitfalls

- **Assuming `gcloud config set` always edits the "default" configuration.** It edits whichever one is
  currently active, if you activated `staging` last week and forgot, `gcloud config set project foo` just
  silently changed `staging`, not `default`.
- **Baking a personal `~/.config/gcloud` into a shared image or volume.** See the CAUTION above, `credentials.db`
  is a live, reusable credential, not a piece of harmless local cache.
- **Forgetting a component is opt-in.** A `(BETA)`/`(ALPHA)` command failing with "invalid choice" almost
  always means `gcloud components install beta`/`alpha`, not a typo in the command itself.
- **Expecting a saved configuration's region/zone to track a project's cloud-side default.** It doesn't,
  see the IMPORTANT callout, local config state and Console-set project defaults are independent.
- **Running `gcloud init` non-interactively on a CI runner and having it hang.** `gcloud init` expects a
  human; use the direct `auth login --no-launch-browser` / `auth activate-service-account` +
  `config set` sequence instead, as shown above.

## Exit codes

`0` success, non-zero for any auth failure, missing property that a command required, or malformed flag,
`gcloud` doesn't distinguish "you're not authenticated" from "you're authenticated but missing a required
property" in the exit code itself, `gcloud info` (or `--verbosity=debug` on the failing command) is the
fastest way to tell the two apart.

## When to reach for something else

For team-shared, reviewable environment bootstrapping (which project, which region, which service account a
given pipeline should run as), prefer committing that as plain values in a Terraform variables file or a
CI system's own environment configuration rather than relying on a specific engineer's local named
configuration, a laptop's `gcloud` state should never be the only place a deployment's target project is
recorded. See page `01` for the authentication and IAM mechanics this page's configurations ultimately point
at.
