Google Cloud Run provides a serverless container platform that scales automatically based on traffic. This guide walks through deploying the Prebid Sales Agent on Cloud Run with Cloud SQL for PostgreSQL as the database backend.
This deployment is well-suited for organizations already in the Google Cloud ecosystem, particularly those using Google Ad Manager as their ad server.
gcloud) installed and authenticatedEnable the required APIs:
gcloud services enable \
run.googleapis.com \
sqladmin.googleapis.com \
artifactregistry.googleapis.com \
secretmanager.googleapis.com \
vpcaccess.googleapis.com \
--project=your-project-id
Create a PostgreSQL instance:
gcloud sql instances create adcp-sales-db \
--database-version=POSTGRES_17 \
--tier=db-f1-micro \
--region=us-central1 \
--storage-size=10GB \
--storage-auto-increase \
--project=your-project-id
Create the database and user:
# Set the default user password
gcloud sql users set-password postgres \
--instance=adcp-sales-db \
--password=your-db-password \
--project=your-project-id
# Create the application database
gcloud sql databases create adcp_sales \
--instance=adcp-sales-db \
--project=your-project-id
Note the instance connection name for later:
gcloud sql instances describe adcp-sales-db \
--format="value(connectionName)" \
--project=your-project-id
# Output: your-project-id:us-central1:adcp-sales-db
gcloud artifacts repositories create adcp-sales \
--repository-format=docker \
--location=us-central1 \
--project=your-project-id
# Configure Docker for Artifact Registry
gcloud auth configure-docker us-central1-docker.pkg.dev
# Build the image
docker build -t us-central1-docker.pkg.dev/your-project-id/adcp-sales/adcp-sales-agent:latest .
# Push the image
docker push us-central1-docker.pkg.dev/your-project-id/adcp-sales/adcp-sales-agent:latest
Store sensitive values in Secret Manager:
# Database URL
echo -n "postgresql+asyncpg://postgres:your-db-password@/adcp_sales?host=/cloudsql/your-project-id:us-central1:adcp-sales-db" | \
gcloud secrets create adcp-database-url --data-file=- --project=your-project-id
# Encryption key
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode(), end='')" | \
gcloud secrets create adcp-encryption-key --data-file=- --project=your-project-id
# Google OAuth credentials (for Admin UI)
echo -n "your-oauth-client-id" | \
gcloud secrets create adcp-oauth-client-id --data-file=- --project=your-project-id
echo -n "your-oauth-client-secret" | \
gcloud secrets create adcp-oauth-client-secret --data-file=- --project=your-project-id
A VPC connector allows Cloud Run to connect to Cloud SQL via private IP:
gcloud compute networks vpc-access connectors create adcp-vpc-connector \
--region=us-central1 \
--range=10.8.0.0/28 \
--project=your-project-id
Deploy the container with all configuration:
gcloud run deploy adcp-sales \
--image=us-central1-docker.pkg.dev/your-project-id/adcp-sales/adcp-sales-agent:latest \
--platform=managed \
--region=us-central1 \
--port=8080 \
--memory=1Gi \
--cpu=1 \
--min-instances=1 \
--max-instances=5 \
--concurrency=80 \
--timeout=300 \
--allow-unauthenticated \
--add-cloudsql-instances=your-project-id:us-central1:adcp-sales-db \
--vpc-connector=adcp-vpc-connector \
--set-env-vars="ENVIRONMENT=production,PRODUCTION=true,ADCP_SALES_PORT=8080,ADCP_SALES_HOST=0.0.0.0,ADCP_MULTI_TENANT=false,ADCP_AUTH_TEST_MODE=false,SKIP_NGINX=true,SKIP_CRON=false,CREATE_DEMO_TENANT=true" \
--set-secrets="DATABASE_URL=adcp-database-url:latest,ENCRYPTION_KEY=adcp-encryption-key:latest,GAM_OAUTH_CLIENT_ID=adcp-oauth-client-id:latest,GAM_OAUTH_CLIENT_SECRET=adcp-oauth-client-secret:latest" \
--project=your-project-id
SKIP_NGINX=true on Cloud Run because Cloud Run handles TLS termination and load balancing. The internal nginx reverse proxy is not needed.
After deployment, note the service URL:
gcloud run services describe adcp-sales \
--region=us-central1 \
--format="value(status.url)" \
--project=your-project-id
# Output: https://adcp-sales-xxxxxx-uc.a.run.app
# Health check
curl https://adcp-sales-xxxxxx-uc.a.run.app/health
# View logs
gcloud run services logs read adcp-sales \
--region=us-central1 \
--project=your-project-id
Access the Admin UI at https://adcp-sales-xxxxxx-uc.a.run.app/admin.
Map a custom domain to the Cloud Run service:
gcloud run domain-mappings create \
--service=adcp-sales \
--domain=adcp.yourcompany.com \
--region=us-central1 \
--project=your-project-id
Follow the output instructions to add the required DNS records. Cloud Run provisions a managed TLS certificate automatically.
Verify the mapping:
gcloud run domain-mappings describe \
--domain=adcp.yourcompany.com \
--region=us-central1 \
--project=your-project-id
Create a dedicated service account for the Cloud Run service:
# Create service account
gcloud iam service-accounts create adcp-sales-runner \
--display-name="ADCP Sales Agent Runner" \
--project=your-project-id
# Grant Cloud SQL client role
gcloud projects add-iam-policy-binding your-project-id \
--member="serviceAccount:adcp-sales-runner@your-project-id.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
# Grant Secret Manager accessor role
gcloud projects add-iam-policy-binding your-project-id \
--member="serviceAccount:adcp-sales-runner@your-project-id.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
# Update the Cloud Run service to use this service account
gcloud run services update adcp-sales \
--service-account=adcp-sales-runner@your-project-id.iam.gserviceaccount.com \
--region=us-central1 \
--project=your-project-id
If using Google Ad Manager, set the GOOGLE_APPLICATION_CREDENTIALS environment variable to point to a service account key file, or use Workload Identity Federation:
# Store the service account key as a secret
gcloud secrets create gam-service-account \
--data-file=path/to/gam-service-account.json \
--project=your-project-id
# Mount it as a volume in Cloud Run
gcloud run services update adcp-sales \
--set-secrets="/app/gam-credentials.json=gam-service-account:latest" \
--set-env-vars="GOOGLE_APPLICATION_CREDENTIALS=/app/gam-credentials.json,GCP_PROJECT_ID=your-project-id" \
--region=us-central1 \
--project=your-project-id
| Parameter | Value | Description |
|---|---|---|
--min-instances |
1 | Minimum running instances (set to 0 for scale-to-zero) |
--max-instances |
5 | Maximum instances under load |
--concurrency |
80 | Requests per instance before scaling out |
--cpu |
1 | CPU allocation per instance |
--memory |
1Gi | Memory allocation per instance |
--timeout |
300 | Maximum request duration in seconds |
--min-instances=1 for production to avoid cold starts. The Sales Agent needs to run Alembic migrations and establish database connections on startup, which can take 10-30 seconds.
gcloud run services update adcp-sales \
--min-instances=2 \
--max-instances=10 \
--memory=2Gi \
--cpu=2 \
--region=us-central1 \
--project=your-project-id
For high-traffic deployments, consider the Cloud SQL Auth Proxy with connection pooling, or use the built-in PgBouncer support:
gcloud run services update adcp-sales \
--set-env-vars="USE_PGBOUNCER=true" \
--region=us-central1 \
--project=your-project-id
Upgrade the Cloud SQL instance for production workloads:
gcloud sql instances patch adcp-sales-db \
--tier=db-custom-2-4096 \
--availability-type=REGIONAL \
--backup-start-time=02:00 \
--enable-bin-log \
--project=your-project-id
Enable automated backups:
gcloud sql instances patch adcp-sales-db \
--backup-start-time=02:00 \
--retained-backups-count=7 \
--project=your-project-id
| Component | Specification | Estimated Monthly Cost |
|---|---|---|
| Cloud Run | 1 instance, 1 vCPU, 1 GB, always-on | ~$15 |
| Cloud Run | 2 instances, 1 vCPU, 1 GB, always-on | ~$30 |
| Cloud SQL | db-f1-micro, 10 GB | ~$10 |
| Cloud SQL | db-custom-2-4096, 20 GB, HA | ~$70 |
| Artifact Registry | 1 GB storage | ~$0.10 |
| Secret Manager | 5 secrets, 10K accesses | ~$0.03 |
| VPC Connector | Serverless VPC Access | ~$7 |
| Custom domain + TLS | Managed certificates | $0 |
| Total (minimal) | ~$32/mo | |
| Total (production) | ~$107/mo |
--min-instances=0) can reduce costs significantly for low-traffic deployments but introduces cold start latency.
# View detailed deployment logs
gcloud run services logs read adcp-sales --region=us-central1 --project=your-project-id --limit=50
Common causes:
artifactregistry.reader rolesecretmanager.secretAccessor role--timeout and verify the /health endpoint works locally--add-cloudsql-instances flag matches your instance connection nameDATABASE_URL uses the Unix socket path: ?host=/cloudsql/project:region:instancegcloud sql instances describe adcp-sales-db --project=your-project-id--min-instances=1 or higher to keep instances warm--cpu to speed up startup