Your API key stops living in your source code and starts living in Secret Manager.

Secret Manager is a Google Cloud service that keeps API keys and passwords out of your code, for only the apps you allow.

For developers new to Google Cloud · about 10 minutes

You need

  • A Google Cloud project, the space that holds your resources and bill.
  • The gcloud command-line tool, or Cloud Shell, the browser terminal that already has it.
  • One key to move.
A hard-coded key moving out of source code and into Secret ManagerA source file called app/config.js loses its hard-coded key line. The value becomes version 1 of a secret named api-key. The source file keeps only a reference to an environment variable.app/config.jsapiKey: "PASTE-YOUR-KEY-HERE"apiKey: process.env.API_KEYapi-key0203v1VERSIONLATEST

Why not keep the key in your code?

One thing to rememberCode holds the name of the secret. Google holds the value.

A pasted key spreading through copies, next to a single stored secretOn the left, a config file holds the key itself, and the same key rides along in every clone and branch stacked behind it. On the right, one config file holds only the secret's name, and the value sits in a single place that Google manages.key = "PASTE-KEY-HERE"every clone, branch and logkey = env.API_KEYapi-keyv1 v2 v3one value, one place
  • Code gets copied.Every clone, branch and build log carries the key. Deleting the line later does not remove it from git history.
  • Changing the key gets easy.You store the new value in one place and point the app at it. No code change.
  • You choose who can read it.Only the apps you name get the value.

The 20-second summary

  1. A secret is a named box.It has a name and a list of who may read it. On its own it holds no value.
  2. Each value is a numbered version.Adding a value creates version 1, then 2, then 3. A version never changes once it exists.
  3. Apps should name a version number.latest is a label that moves to the newest version. Why that bites.
  4. You choose who can read it.Your app gets its own identity. You give that identity read access to this one secret.

Nine words you will meet

Project
The Google Cloud container for your resources, permissions and bill.
gcloud
Google Cloud’s command-line tool. Cloud Shell, the terminal in the Cloud console, has it.
IAM
Identity and Access Management, Google Cloud’s permission system: who can do what to which resource.
Service account
An identity for software, not a person. Your app runs as one, and you grant it permissions.
Cloud Run
Google’s service for running your app in containers. The examples use it.
Client library
Google’s code package for calling Secret Manager from your app, such as google-cloud-secret-manager for Python.
Version
One stored value of a secret, numbered 1, 2, 3. It never changes after you add it.
latest
A label for the most recently created version. How it can fail.
Audit log
Google Cloud’s record of who did what, and when.

A secret holds versions. Versions never change.

One thing to rememberAdding a value never edits the old one. It creates a new numbered version.

Each value you add becomes its own numbered version, fixed from then on.

projects/my-project/secrets/api-key

    Version details — sample data

    Name
    —
    State
    —
    Created
    —
    Payload
    —

    How latest works

    latest is a label, not a version. It points at the most recently created version. It will never fall back to an older version: if the newest one is disabled or destroyed, the read fails.

    For production, pin a number, such as versions/3. It only changes when you change it.

    Move one key, start to finish

    One thing to rememberSign in, create, add the value, let one app read it, connect, check.

    Swap api-key, my-project and my-service for your own names.

    1. Sign in and pick your project

      Terminal
      gcloud auth login
      gcloud config set project my-project
      gcloud services enable secretmanager.googleapis.com

      gcloud init does the first two in one step. The last line turns Secret Manager on for your project. You also need a role that can create secrets, such as roles/secretmanager.admin. If you created the project, you are its Owner and have both.

    2. Create the secret

      Terminal
      gcloud secrets create api-key \
        --replication-policy=automatic

      This makes an empty, named secret. automatic lets Google choose where copies live. You cannot change that later; see the fine print.

    3. Add the value

      Terminal
      read -rs API_KEY   # paste the key, press Enter; it stays hidden
      printf '%s' "$API_KEY" | gcloud secrets versions add api-key --data-file=-
      unset API_KEY

      This stores version 1. Use printf '%s', not echo. echo adds a newline, and Secret Manager stores it as part of the value. The service you call can then reject the key, which shows up as an auth failure. Google’s docs use echo -n for the same reason.

    4. Let your app read it

      Terminal
      gcloud iam service-accounts create app-runtime
      
      gcloud run services update my-service \
        --service-account=app-runtime@my-project.iam.gserviceaccount.com
      
      gcloud secrets add-iam-policy-binding api-key \
        --member="serviceAccount:app-runtime@my-project.iam.gserviceaccount.com" \
        --role="roles/secretmanager.secretAccessor"

      Your app runs as a service account. Unless you pick one, Cloud Run uses the Compute Engine default, PROJECT_NUMBER-compute@developer.gserviceaccount.com. Google recommends a dedicated one: create it, attach it (a new revision, or do it at deploy), then grant it this one secret.

    5. Connect your app: three ways

      1. Environment variable. The default. Cloud Run reads the secret before each instance starts and sets API_KEY.
      2. Mounted file. Cloud Run shows the secret as a file and reads it from Secret Manager each time your code reads the file.
      3. Client library. Your code asks Secret Manager itself, through the client library. Use it off Cloud Run.
      Environment variable, set at startupBefore each Cloud Run instance starts, the platform reads the pinned secret version and copies it into the environment. A running instance keeps its copy.secretapi-keyinstance startreads secretcontainerAPI_KEY=…running copy stays

      Environment variable: copied in at startup

      Client library, read on demandThe running app calls the Secret Manager API itself, which checks permissions and returns the version the code names. The app may cache the result.secretapi-keyAPIchecks IAMcontainerapp may cachereads what you name

      Client library: read when your code asks

      Cloud Run

      gcloud run services update my-service \
        --update-secrets=API_KEY=api-key:1

      Pins API_KEY to version 1. This command deploys a new revision: a new release of your service. For a file, use a path: /secrets/api/key=api-key:1.

      Python

      from google.cloud import secretmanager
      
      client = secretmanager.SecretManagerServiceClient()
      name = "projects/my-project/secrets/api-key/versions/1"
      api_key = client.access_secret_version(name=name).payload.data.decode("UTF-8")

      Install google-cloud-secret-manager first. It names version 1, the same pin. The value arrives as bytes, so decode it, and cache it.

    6. Check it worked

      Terminal
      gcloud secrets versions list api-key

      You should see version 1, enabled. Then try the feature that uses the key; if it fails, check the logs.

      Last, delete the key from your code. Git history still has it, so treat it as leaked and rotate it.

    Who can read it

    One thing to rememberGrant roles/secretmanager.secretAccessor on the one secret an app needs, never across the whole project.

    Every read is checked against the secret’s IAM permission list.

    How an access request is checkedA read request is compared against the secret's permission list and is either allowed or refused.THE SECRETread requestIAMCHECKED

    Try a caller

      Pick a caller to see whether the request is allowed.

      Data Access log (simulated)
      TimePrincipalMethodResult

      No requests yet.

      Each read is an AccessSecretVersion call. Data Access audit logs, which record reads, are off by default. Enable them to see who read a secret.

      secretAccessor reads the value. What a running app needs, granted on one secret.

      viewer sees details, not values. Names, versions and states only.

      secretVersionManager rotates without reading. It adds, disables and destroys versions. Good for a rotation job.

      admin does everything, including permissions. For people who set secrets up, never apps.

      Rotate safely

      One thing to rememberAdd the new key before you remove the old one. Disable first. Destroy last.

      Swap in a new key with no downtime. Only the last step cannot be undone.

      1. Step 1 of 6

        Get a new key from the provider

        Create a second key where you got the first. Leave the old one working for now.

      2. Step 2 of 6

        Add it as a new version

        Version 2 appears. Version 1 is untouched, and the app keeps reading it because it is pinned to 1.

        Command
        read -rs NEW_KEY
        printf '%s' "$NEW_KEY" | gcloud secrets versions add api-key --data-file=-
        unset NEW_KEY
      3. Step 3 of 6

        Point the app at it

        Change the pin to version 2. This command deploys a new revision, and new instances start with the new key.

        Command
        gcloud run services update my-service \
          --update-secrets=API_KEY=api-key:2
      4. Step 4 of 6

        Check that it works

        Use the feature that needs the key and watch for errors. If it fails, set the pin back to 1. Nothing is lost.

        Command
        gcloud run revisions list --service=my-service
      5. Step 5 of 6

        Disable the old version

        Reads of version 1 now fail, which flushes out anything still using it. Watch for a few days. enable brings it back.

        Command
        gcloud secrets versions disable 1 --secret=api-key
      6. Step 6 of 6

        Revoke the old key, then destroy it

        Revoke the old key at the provider first. That kills every copy, including the one in git history. Then destroy version 1.

        Command
        gcloud secrets versions destroy 1 --secret=api-key

        Destroy is immediate and permanent by default

        Its number is never reused. Only a destruction delay set on the secret before you destroy lets you restore it; see the fine print.

      All commands

      One thing to rememberNothing new here. It is every command from this page, plus a few for cleanup.

      More commands

      Sign in and enable the API

      gcloud auth login
      gcloud config set project my-project
      gcloud services enable secretmanager.googleapis.com

      One-time setup. Step 0.

      Create a secret

      gcloud secrets create api-key --replication-policy=automatic

      An empty, named secret. Step 1.

      Add a version

      printf '%s' "$API_KEY" | gcloud secrets versions add api-key --data-file=-
      gcloud secrets versions add api-key --data-file=./api-key.txt

      From a variable or a file. Files often end in a newline too. Step 2.

      Let one app read it

      gcloud iam service-accounts create app-runtime
      gcloud run services update my-service --service-account=app-runtime@my-project.iam.gserviceaccount.com
      gcloud secrets add-iam-policy-binding api-key \
        --member="serviceAccount:app-runtime@my-project.iam.gserviceaccount.com" \
        --role="roles/secretmanager.secretAccessor"

      Create, attach, then grant on this one secret only. Step 3.

      Pin it on Cloud Run

      gcloud run services update my-service --update-secrets=API_KEY=api-key:1

      Deploys a new revision. Step 4.

      List secrets and versions

      gcloud secrets list
      gcloud secrets versions list api-key

      Numbers, states and dates. Never values.

      Read a value into a file

      gcloud secrets versions access 1 --secret=api-key --out-file=./api-key.txt

      Without --out-file, the value prints to your terminal and its scrollback.

      Disable and re-enable

      gcloud secrets versions disable 1 --secret=api-key
      gcloud secrets versions enable 1 --secret=api-key

      Reversible. Rotate, step 5.

      Set a delay, then destroy

      # set this before you destroy
      gcloud secrets update api-key --version-destroy-ttl=86400s
      gcloud secrets versions destroy 1 --secret=api-key

      Set this before you destroy: the one-day delay only covers versions destroyed after it is on. Fine print.

      Delete the whole secret

      gcloud secrets delete api-key

      Removes every version and the permission list, for good. For retiring a secret, not rotating it.

      Common mistakes

      One thing to rememberA secret is not a file you edit. It is a list of versions you add to.

      echo is fine for piping a value in.

      echo stores a newline

      Use printf '%s'. Why, in step 2.

      latest is safe for production.

      Pin a version number

      A pin like api-key:2 changes only when you move it. How latest works.

      Project-wide access is simpler.

      Grant access on one secret

      A project-level grant opens every secret in the project. Bind it on one secret.

      Adding a version updates my running app.

      The pin decides what the app reads

      A pinned app keeps its version until you move the pin. Rotate safely.

      Deleting the line removed the leak.

      Git history keeps the key

      Anyone with the repository can find it. Revoke it at the provider (step 6).

      More mistakes
      Disable and destroy are about the same.

      Disable is reversible. Destroy is not.

      Disable, watch for failures, and destroy last. Rotate safely.

      I will log the value just while I debug.

      A logged secret is a leaked secret

      Log the version name, such as api-key/versions/3, never the value.

      Reading on every request is fine.

      Each read costs a round trip

      Uncached reads add latency and are billed after the free allowance. Cache the value. Fine print.

      Deleting and recreating is a clean rotation.

      Delete removes the history too

      It takes every version and the permission list with it. Rotate by adding a version.

      Replication is a detail I can fix later.

      Replication is fixed at creation

      Changing it means a new secret. Fine print.

      A rotation schedule rotates the key for me.

      Schedules only send a reminder

      They publish a Pub/Sub message on time. Your own code has to do the rotation.

      Hand it to your coding assistant

      Paste this into Claude Code, Cursor or another coding assistant. It applies this guide to your repository and forbids showing any part of a secret.

      Help me move hardcoded credentials in this repository into Google Cloud Secret Manager without exposing any secret value.
      
      RULES
      1. Show me the full plan before edits, and wait for my approval.
      2. Never print a credential, not even part of one. Report each finding as file:line, the kind of secret, and a label that contains no characters from the value.
      3. Never fetch or echo a secret payload into this conversation. Do not run gcloud secrets versions access just to show me a value.
      4. No destructive operations and no history rewrites. Do not delete secrets or versions, do not force push, and do not rewrite git history.
      5. Stop and ask if the project, the service identity, the rollback plan or my approval is missing.
      
      STEPS
      1. Inventory, read-only: search source, config, .env* files, deployment manifests, CI settings, build arguments, scripts and docs.
      2. Confirm the Google Cloud project with me, then enable the API:
         gcloud services enable secretmanager.googleapis.com
      3. For each secret, propose a kebab-case name, then create it and add the value:
         gcloud secrets create NAME --replication-policy=automatic
         printf '%s' "$VALUE" | gcloud secrets versions add NAME --data-file=-
         Use printf '%s', not echo: echo adds a newline that becomes part of the stored value.
      4. Grant roles/secretmanager.secretAccessor on that one secret only, never project-wide, and only to the service account that runs the app:
         gcloud secrets add-iam-policy-binding NAME \
           --member="serviceAccount:SERVICE_ACCOUNT" \
           --role="roles/secretmanager.secretAccessor"
      5. Change the code to read the value from an environment variable, a mounted secret file, or the Secret Manager client library. No hard-coded fallback: if the value is missing, fail closed with a clear error.
      6. Make sure .env* is gitignored, and keep only names, never values, in .env.example.
      7. For Cloud Run, pin a numeric version, for example NAME:2, not latest. To rotate: add a new version, then update the pin; that update deploys a new revision.
      8. Run this project's build and tests, and confirm the app starts with no hard-coded values left.
      9. Finish with a rotation checklist: every credential that ever touched git history is still in that history, so rotate or revoke it at its provider. Disable old versions before destroying them.

      Six questions

      One thing to rememberIf you can answer these six, you have the model.

      The short version

      Code holds the name; Secret Manager holds the value as numbered versions. Pin your app to a number and let it read that one secret only.

      To rotate: add a version, move the pin, check it works, disable the old version, then revoke and destroy it.

      Fine print

      • Audit logs. Admin Activity logs record changes and are always on. Reads (AccessSecretVersion) go to Data Access logs; enable them in the project, folder or organization audit configuration.
      • Replication. Set at creation; it cannot change. User-managed replication can meet location rules. If access must stay in one region, use regional secrets.
      • Destruction delay. Destroy is immediate by default. Set --version-destroy-ttl (1 to 1,000 days) on the secret before you destroy; destroy then disables and schedules the version, and an admin can restore it until then.
      • Cost. Access operations have a monthly free allowance: 10,000 per billing account, per Google’s current pricing. It may change.

      Every name and value here is a placeholder. The page makes no network calls.