Environment & Configuration Management
Verified against Google Cloud SDK 553.0.0, verified via `gcloud topic configurations`, `gcloud info`, · official docs
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:
gcloud version # confirm the CLI and every installed component's version
gcloud components update # keep the CLI and all installed components in lockstepTip
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#
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 |
gcloud info --format="value(config.paths.global_config_dir)" # the actual resolved path, whatever OS you're onCLOUDSDK_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#
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 machinegcloud 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"#
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 passedA 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#
# ~/.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-aPlain 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#
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/zoneProperties 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#
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#
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 wantgcloud 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#
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 inconsistentcore, 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#
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 firstFull 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 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# 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:latestgcloud 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 registriesgcloud 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:
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, andcompute/regionfor 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 listshows the expected account/project pairing for every client before running anything destructive, a wrong-clientterraform applyorgcloud ... deleteis 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#
# .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 identitySetting 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 setalways edits the "default" configuration. It edits whichever one is currently active, if you activatedstaginglast week and forgot,gcloud config set project foojust silently changedstaging, notdefault. - Baking a personal
~/.config/gcloudinto a shared image or volume. See the CAUTION above,credentials.dbis 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 meansgcloud 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 initnon-interactively on a CI runner and having it hang.gcloud initexpects a human; use the directauth login --no-launch-browser/auth activate-service-account+config setsequence 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.