HomeBlog

Secure FastAPI Environment Variables on Cloud Run with Secret Manager

7 min read
Google Secret Manager logo integrated with FastAPI and Google Cloud Run logos, illustrating a secure connection.
By David Muraya • September 3, 2025

In my previous guide on setting up a simple CI/CD pipeline for FastAPI, we used --set-env-vars to pass configuration to Cloud Run. While convenient, it's not a secure way to manage secrets like API keys or database passwords. For production environments on Google Cloud, the recommended practice is to use Google Secret Manager.

In local development, we often use a .env file to store these details. In production, a dedicated secret management tool is essential. This guide will show you how to use Google Secret Manager to securely provide secrets to your FastAPI application running on Cloud Run.

1. Enable the Secret Manager API

First, you need to enable the Secret Manager API for your Google Cloud project.

  1. Go to the Google Cloud Console.
  2. Navigate to "APIs & Services" > "Library".
  3. Search for "Secret Manager API".
  4. Click on it and ensure it's "Enabled".

To allow Cloud Run to access the secret, the Cloud Run service identity must have the following role roles/secretmanager.secretAccessor.

2. Create a Secret

Now, let's create a secret to store a value, like a database password. To do this, you'll need the "Secret Manager Admin" (roles/secretmanager.admin) IAM role on your project.

  1. In the Cloud Console, navigate to "Security" > "Secret Manager".
  2. Click "Create Secret".
  3. Give your secret a name (e.g., POSTGRES_PASSWORD).
  4. In the "Secret value" field, enter the sensitive information.
  5. Click "Create Secret".

Secret Manager automatically versions your secrets. Each time you update the value, a new version is created. This allows you to pin your application to a specific version of a secret for stability.

3. Make Secrets Available to Cloud Run

You can mount secrets into your Cloud Run service as environment variables. When you do this, the secret's value is exposed to your application just like any other environment variable. This works perfectly with the Pydantic Settings approach described in my article on Centralizing Your FastAPI Configuration Settings.

When a secret is mounted as an environment variable, Cloud Run retrieves its value before the instance starts. If the secret cannot be fetched - due to a permission error, for example - the instance will fail to start. This prevents your application from running without the credentials it needs.

When configuring your Cloud Run service, you can choose to mount a specific version of a secret or always use the latest version. Google recommends pinning to a specific version for production services to avoid unexpected changes.

4. Update Your cloudbuild.yaml

To use secrets in your Cloud Build pipeline, you need to declare them in your cloudbuild.yaml file. This is useful for steps that need access to secrets, like running database migrations with Alembic.

Here’s an example of a cloudbuild.yaml that makes secrets available to a migration step.

steps:
  # Step 1: Build the Docker image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA', '.']

  # Step 2: Run Database Migrations using the newly built image
  # secretEnv makes the secrets available as environment variables for this step only
  - name: 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA'
    entrypoint: 'alembic'
    args: ['upgrade', 'head']
    secretEnv: ['POSTGRES_USER', 'POSTGRES_PASSWORD', 'POSTGRES_HOST', 'POSTGRES_DATABASE_NAME']

  # Step 3: Push the Docker image to Google Container Registry
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA']

  # Step 4: Deploy the Docker image to Google Cloud Run
  # This step mounts the secrets directly into the running service
  - name: 'gcr.io/cloud-builders/gcloud'
    args:
      - 'run'
      - 'deploy'
      - 'my-app' # Your service name
      - '--image=gcr.io/$PROJECT_ID/my-app:$COMMIT_SHA'
      - '--region=us-central1'
      - '--project=$PROJECT_ID'
      # Mount secrets into the Cloud Run service
      - '--set-secrets=POSTGRES_USER=POSTGRES_USER:latest,POSTGRES_PASSWORD=POSTGRES_PASSWORD:latest,POSTGRES_HOST=POSTGRES_HOST:latest,POSTGRES_DATABASE_NAME=POSTGRES_DATABASE_NAME:latest'

# This section makes secrets available to the build steps
availableSecrets:
  secretManager:
    - versionName: projects/$PROJECT_ID/secrets/POSTGRES_USER/versions/latest
      env: 'POSTGRES_USER'
    - versionName: projects/$PROJECT_ID/secrets/POSTGRES_PASSWORD/versions/latest
      env: 'POSTGRES_PASSWORD'
    - versionName: projects/$PROJECT_ID/secrets/POSTGRES_HOST/versions/latest
      env: 'POSTGRES_HOST'
    - versionName: projects/$PROJECT_ID/secrets/POSTGRES_DATABASE_NAME/versions/latest
      env: 'POSTGRES_DATABASE_NAME'

options:
  logging: CLOUD_LOGGING_ONLY

There are two key parts here:

  1. availableSecrets: This block at the bottom of the file tells Cloud Build which secrets from Secret Manager to fetch. The env key assigns a short name to be used within the build pipeline.
  2. --set-secrets flag: In the run deploy step, this flag tells Cloud Run to mount the specified secrets into the running container as environment variables. The format is [ENV_VAR_NAME]=[SECRET_NAME]:[VERSION]. This is the secure and recommended way to expose secrets to your final application.

The example cloudbuild.yaml also shows how to run database migrations as a build step. For a detailed walkthrough, you can read my guide on running database migrations with Alembic in Google Cloud Build. To keep your deployments fast and cost-effective, it's also important to create optimized, small images, which I cover in my guide on slimmer FastAPI Docker images with multi-stage builds.

By using Secret Manager, you separate your sensitive data from your source code and build configuration, leading to a more secure and manageable deployment process.


FAQ

Q: Why is using Secret Manager better than --set-env-vars? A: The --set-env-vars flag stores your variables in plain text within the service configuration. Anyone with permission to view the Cloud Run service can see them. Secret Manager stores secrets in an encrypted format and provides access through tightly controlled IAM permissions, which is much more secure.

Q: Should I use the latest version or a pinned version of a secret? A: For production, it's safer to pin to a specific version (e.g., POSTGRES_PASSWORD:1). This prevents your application from breaking if a secret is updated unexpectedly. Use latest for development or non-critical services where you always want the newest value.

Q: How does my FastAPI application read these secrets? A: Your application reads them just like any other environment variable. If you've configured Pydantic Settings to read from environment variables, it will work automatically without any code changes.

Q: What happens if my Cloud Build step fails with a permission error? A: This usually means the Cloud Build service account ([PROJECT_NUMBER]@cloudbuild.gserviceaccount.com) does not have the "Secret Manager Secret Accessor" role. You need to grant this role in the IAM & Admin section of the Cloud Console.

If you're new to deploying FastAPI applications with CI/CD on Google Cloud, check out my guide: Simple CI/CD for Your FastAPI App with Google Cloud Build and Cloud Run. This article covers how to automate builds and deployments using Cloud Build and Cloud Run, providing a solid foundation for integrating secrets management as described here.

Share This Article

About the Author

David Muraya is a Solutions Architect specializing in Python, FastAPI, and Cloud Infrastructure. He is passionate about building scalable, production-ready applications and sharing his knowledge with the developer community. You can connect with him on LinkedIn.

Related Blog Posts

Enjoyed this blog post? Check out these related posts!

Building a Flexible Memcached Client for FastAPI

Building a Flexible Memcached Client for FastAPI

Flexible, Safe, and Efficient Caching for FastAPI with Memcached and aiomcache

Read More..

Managing Background Tasks in FastAPI: BackgroundTasks vs ARQ + Redis

Managing Background Tasks in FastAPI: BackgroundTasks vs ARQ + Redis

A practical guide to background processing in FastAPI, comparing built-in BackgroundTasks with ARQ and Redis for scalable async job queues.

Read More..

Centralizing Your FastAPI Configuration Settings with Pydantic Settings

Centralizing Your FastAPI Configuration Settings with Pydantic Settings

How to Organize and Load FastAPI Configuration Settings from a .env File Using Pydantic Settings

Read More..

Simple CI/CD for Your FastAPI App with Google Cloud Build and Cloud Run

Simple CI/CD for Your FastAPI App with Google Cloud Build and Cloud Run

Push code, deploy automatically: A simple CI/CD guide for your web app.

Read More..

Contact Me

Have a project in mind? Send me an email at hello@davidmuraya.com and let's bring your ideas to life. I am always available for exciting discussions.

© 2025 David Muraya. All rights reserved.