Skip to content

GCP Cloud Run

Tested with: Cloud Run (gen2) · gcloud 500 · Secret Manager · PostgreSQL 17

  1. Store the connection string in Secret Manager and mount it as an environment variable.

    shell
    printf '%s' 'postgresql://app:<password>@pg-7f3k.us-east.databasezy.com:5432/app?sslmode=verify-full' \
    | gcloud secrets create databasezy-pg-prod --data-file=-
    gcloud run deploy api --image gcr.io/acme/api:1.4.2 --region us-east1 \
    --set-secrets DATABASE_URL=databasezy-pg-prod:latest \
    --network main --subnet private --vpc-egress all-traffic
  2. --vpc-egress all-traffic with Direct VPC egress sends outbound traffic through your VPC; add a Cloud NAT with a static address and allow it.

    shell
    gcloud compute addresses create cloudrun-nat --region us-east1
    gcloud compute routers nats create main-nat --router main-router --region us-east1 \
    --nat-external-ip-pool cloudrun-nat --nat-all-subnet-ip-ranges
    zb api PUT /v1/orgs/{org}/instances/pg-7f3k/network -d '{"allow_cidrs": ["34.0.0.7/32"]}' # replaces the list
  3. Run migrations as a Cloud Run Job before deploying the new revision.

    shell
    gcloud run jobs create migrate --image gcr.io/acme/api:1.4.2 --region us-east1 \
    --set-secrets DATABASE_URL=databasezy-pg-prod:latest --command npx --args prisma,migrate,deploy \
    --network main --subnet private --vpc-egress all-traffic
    gcloud run jobs execute migrate --wait

Cloud Run instances handle many concurrent requests each; a pool of max: 5 per instance with --concurrency 80 is a good start. Use the pooled endpoint when max-instances × pool size approaches the size’s max_connections.