ServicesAboutNotesContact Get in touch →
EN FR
Note

direnv for Multi-Client GCP Credential Management

Automate per-project GCP credential loading with direnv — .envrc configuration, the four-variable pattern, and a five-minute setup for each new client.

Planted
gcpdbtdata engineeringautomation

Setting four environment variables every time you switch client directories gets old fast. direnv solves this by loading environment variables automatically when you enter a directory and unloading them when you leave. Combined with the four-variable isolation pattern, this means each client’s credentials load when you cd into their project and disappear when you leave.

How direnv Works

direnv is a shell extension that watches for .envrc files. When you navigate into a directory containing an .envrc, direnv sources it and sets the declared environment variables in your shell. When you navigate out, it reverses the changes.

The first time you enter a directory with a new .envrc, direnv requires you to explicitly trust it (direnv allow). After that, it loads automatically. This safety step prevents arbitrary code execution from cloned repositories.

The .envrc Pattern

A complete .envrc for a single client project looks like this:

~/projects/acme-analytics/.envrc
export CLOUDSDK_CONFIG="$HOME/.config/gcloud-acme"
export CLOUDSDK_CORE_PROJECT=acme-analytics-prod
export GOOGLE_CLOUD_PROJECT=acme-analytics-prod
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.gcp-keys/acme-dbt-runner.json"
# dbt-specific
export DBT_TARGET=prod
export DBT_PROFILES_DIR="$HOME/.dbt"

When you cd into ~/projects/acme-analytics/, direnv loads these variables. Your gcloud CLI, Python client libraries, dbt, and anything else reading standard GCP environment variables will use the Acme credentials. When you cd out, they’re unloaded.

Every client directory gets its own .envrc with its own values. Switching clients is just switching directories.

Why Four Variables

Two pairs of variables handle two independent systems:

gcloud CLI pair:

  • CLOUDSDK_CONFIG — the directory containing all gcloud state (credentials, config, ADC file, logs)
  • CLOUDSDK_CORE_PROJECT — the default project for gcloud commands

Client library pair:

  • GOOGLE_APPLICATION_CREDENTIALS — path to a service account key file for ADC
  • GOOGLE_CLOUD_PROJECT — the default project for Python, dbt, Terraform, and other client libraries

Skipping either pair means something targets the wrong project. A setup with only the gcloud pair will have gcloud pointing at the right client while dbt run uses whatever ADC happens to be set globally. A setup with only the client library pair does the reverse. See GCP Application Default Credentials for the full picture on why these two systems are separate.

Setting Up a New Client

The full setup for a new client takes about five minutes:

Terminal window
# 1. Create isolated gcloud config directory
mkdir -p ~/.config/gcloud-newclient
# 2. Activate the service account in that isolated directory
CLOUDSDK_CONFIG=~/.config/gcloud-newclient \
gcloud auth activate-service-account \
--key-file=~/.gcp-keys/newclient-sa.json
# 3. Create the .envrc
cat > ~/projects/newclient-dbt/.envrc << 'EOF'
export CLOUDSDK_CONFIG="$HOME/.config/gcloud-newclient"
export CLOUDSDK_CORE_PROJECT=newclient-warehouse
export GOOGLE_CLOUD_PROJECT=newclient-warehouse
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.gcp-keys/newclient-sa.json"
export DBT_TARGET=prod
EOF
# 4. Trust the .envrc
direnv allow ~/projects/newclient-dbt

After this, navigating into the project directory automatically provides the right credentials with no further action required.

AI Agents Inherit These Variables

A particularly useful property: Claude Code, Cursor, and other IDE-based agents inherit environment variables from the shell that launched them. If you start Claude Code from inside a project directory where direnv has loaded the .envrc, the agent gets the right GCP context automatically.

This means an agent running gcloud bq query or triggering a dbt build will use the correct client’s credentials without any extra configuration. The agent doesn’t need to know about the credential setup — it’s ambient, from the shell.

The limitation is that agents can’t refresh credentials when they expire. If a session runs long enough that tokens expire, the agent fails with auth errors and you need to restart. See AI Agent GCP Auth Constraints for the specific behavior of different agents around credential expiry.

Installation

direnv is available through most package managers:

Terminal window
# macOS
brew install direnv
# Then add to your shell config (.zshrc or .bashrc):
eval "$(direnv hook zsh)" # for zsh
eval "$(direnv hook bash)" # for bash

What Gets Unloaded

When you navigate out of a direnv-managed directory, all exported variables from that .envrc are removed from your shell environment. This means:

  • CLOUDSDK_CONFIG reverts to unset (gcloud goes back to ~/.config/gcloud/)
  • GOOGLE_APPLICATION_CREDENTIALS reverts to unset
  • GOOGLE_CLOUD_PROJECT reverts to unset

If you have a global fallback set in your shell profile (.zshrc), those values resume. If you have nothing set globally, the environment is clean with no GCP context. Either behavior is predictable, which is the point.

Keeping Key Files Safe

The .envrc contains paths to key files but not the key files themselves. Keep the actual key files in ~/.gcp-keys/ with tight permissions and excluded from cloud sync. The .envrc files can safely live in version control since they only reference paths.

Terminal window
chmod 600 ~/.gcp-keys/newclient-sa.json

The .envrc itself should usually be in .gitignore since it contains client-specific configuration. Or keep it in version control if you want the project to be self-documenting — the paths in .envrc only matter on your local machine anyway.