Skip to main content

GCP Deployment

Deploy FastAPI to Google Cloud with zero long-lived credentials using Workload Identity Federation and Application Default Credentials (ADC).

Choose your target

Cloud RunApp EngineGKE + Workload Identity
Cost at idle$0 (scale to zero)~$0 (F1 free tier)~$100+/month (node pool)
Cold start?Yes (mitigable with min-instances)YesNo
IAM authService account bindingService accountWorkload Identity
Container supportNativeCustom runtimeNative
Custom networkingVPC connectorLimitedFull VPC
Best forServerless containersSimple web appsMicroservices / complex routing

Cloud Run is the simplest GCP deployment: push a container, get a URL. Each revision runs with a bound Google Cloud service account — no key files needed.

Build and push to Artifact Registry

# Enable APIs
gcloud services enable run.googleapis.com artifactregistry.googleapis.com

# Create registry
gcloud artifacts repositories create fastapi-repo \
--repository-format=docker \
--location=us-central1

# Build and push
gcloud auth configure-docker us-central1-docker.pkg.dev

docker build -t fastapi-books .
docker tag fastapi-books:latest \
us-central1-docker.pkg.dev/my-project/fastapi-repo/fastapi-books:latest
docker push us-central1-docker.pkg.dev/my-project/fastapi-repo/fastapi-books:latest

Create a dedicated service account

# Create service account for the Cloud Run service
gcloud iam service-accounts create fastapi-books-sa \
--display-name "FastAPI Books Service"

# Grant it access to Secret Manager
gcloud projects add-iam-policy-binding my-project \
--member "serviceAccount:fastapi-books-sa@my-project.iam.gserviceaccount.com" \
--role "roles/secretmanager.secretAccessor"

# Grant access to Cloud SQL (example)
gcloud projects add-iam-policy-binding my-project \
--member "serviceAccount:fastapi-books-sa@my-project.iam.gserviceaccount.com" \
--role "roles/cloudsql.client"

Deploy

gcloud run deploy fastapi-books \
--image us-central1-docker.pkg.dev/my-project/fastapi-repo/fastapi-books:latest \
--region us-central1 \
--service-account fastapi-books-sa@my-project.iam.gserviceaccount.com \
--set-env-vars GCP_PROJECT=my-project \
--allow-unauthenticated \
--min-instances 0 \
--max-instances 10

The --service-account flag binds the IAM service account to the Cloud Run revision. No keyfile, no environment variable credentials.

Access GCP services from FastAPI without credentials

Application Default Credentials (ADC) automatically picks up the Cloud Run service account:

from google.cloud import secretmanager
import os

def get_secret(secret_id: str) -> str:
"""Fetch a secret from Secret Manager using ADC (no credentials needed)."""
client = secretmanager.SecretManagerServiceClient() # ADC resolves automatically
project = os.environ["GCP_PROJECT"]
name = f"projects/{project}/secrets/{secret_id}/versions/latest"
response = client.access_secret_version(request={"name": name})
return response.payload.data.decode("utf-8")

Option B — App Engine

App Engine Standard uses the default App Engine service account bound to your app. Useful for simple APIs that don't need custom networking.

# app.yaml
runtime: python313
entrypoint: uvicorn demo.main:app --host 0.0.0.0 --port $PORT

env_variables:
GCP_PROJECT: "my-project"
gcloud app deploy

Grant the App Engine default service account the roles it needs:

gcloud projects add-iam-policy-binding my-project \
--member "serviceAccount:my-project@appspot.gserviceaccount.com" \
--role "roles/secretmanager.secretAccessor"

Option C — GKE with Workload Identity

GKE Workload Identity maps a Kubernetes service account to a Google Cloud service account, giving each pod its own IAM identity — no node-level credentials.

Enable Workload Identity on the cluster

gcloud container clusters create fastapi-cluster \
--workload-pool=my-project.svc.id.goog \
--region us-central1

# Or enable on an existing cluster
gcloud container clusters update fastapi-cluster \
--workload-pool=my-project.svc.id.goog \
--region us-central1

Create the IAM binding

# Create Google Cloud service account
gcloud iam service-accounts create fastapi-gke-sa \
--display-name "FastAPI GKE Service Account"

# Grant permissions to the Google Cloud SA
gcloud projects add-iam-policy-binding my-project \
--member "serviceAccount:fastapi-gke-sa@my-project.iam.gserviceaccount.com" \
--role "roles/secretmanager.secretAccessor"

# Allow the Kubernetes SA to impersonate the Google Cloud SA
gcloud iam service-accounts add-iam-policy-binding fastapi-gke-sa@my-project.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:my-project.svc.id.goog[default/fastapi-sa]"

Kubernetes manifests

# service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: fastapi-sa
namespace: default
annotations:
iam.gke.io/gcp-service-account: fastapi-gke-sa@my-project.iam.gserviceaccount.com
---
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-books
spec:
template:
spec:
serviceAccountName: fastapi-sa # <-- Workload Identity annotation lives here
containers:
- name: fastapi
image: us-central1-docker.pkg.dev/my-project/fastapi-repo/fastapi-books:latest
ports:
- containerPort: 8000
env:
- name: GCP_PROJECT
value: "my-project"

ADC inside the pod automatically uses the Workload Identity token — your Python code is identical to the Cloud Run example above.


Reading GCP secrets in FastAPI

import os
from contextlib import asynccontextmanager
from typing import AsyncIterator

from google.cloud import secretmanager
from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
client = secretmanager.SecretManagerServiceClient()
project = os.environ["GCP_PROJECT"]

def _get(name: str) -> str:
n = f"projects/{project}/secrets/{name}/versions/latest"
return client.access_secret_version(request={"name": n}).payload.data.decode()

app.state.jwt_secret = _get("jwt-signing-key")
app.state.db_password = _get("db-password")
yield

app = FastAPI(lifespan=lifespan)

No key files, no GOOGLE_APPLICATION_CREDENTIALS env var — ADC resolves the bound service account automatically on Cloud Run, GKE, and App Engine.


Next steps