Skip to main content

Google Cloud Run

Cloud Run is Google Cloud's serverless container platform. It automatically scales based on traffic and you only pay for what you use.

Architecture

┌─────────────────────────────────────────────────┐
│ Cloud Run │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ API Service │ │ Worker Service │ │
│ │ (auto-scale)│ │ (min 1 replica)│ │
│ └──────┬──────┘ └────────┬────────┘ │
└─────────┼─────────────────────────┼─────────────┘
│ │
▼ ▼
┌──────────┐ ┌───────────┐
│Cloud SQL │ │ Memorystore│
│(Postgres)│ │ (Redis) │
└──────────┘ └───────────┘

Prerequisites

  • Google Cloud project with billing enabled
  • gcloud CLI installed and configured
  • Cloud SQL PostgreSQL instance (setup guide)
  • Memorystore Redis instance (or Upstash Redis)

Deploy API Service

1. Deploy from Pre-built Image

gcloud run deploy boards-api \
--image ghcr.io/weirdfingers/boards-backend:latest \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--port 8800 \
--cpu 1 \
--memory 512Mi \
--min-instances 0 \
--max-instances 10 \
--set-env-vars "BOARDS_LOG_FORMAT=json,BOARDS_LOG_LEVEL=info" \
--set-secrets "BOARDS_DATABASE_URL=boards-database-url:latest,BOARDS_REDIS_URL=boards-redis-url:latest,BOARDS_GENERATOR_API_KEYS=boards-api-keys:latest"

2. Configure Cloud SQL Connection

Add the Cloud SQL connection:

gcloud run services update boards-api \
--add-cloudsql-instances PROJECT:REGION:INSTANCE_NAME \
--region us-central1

Update the database URL to use the Unix socket:

# Store in Secret Manager
echo -n "postgresql://user:pass@/boards?host=/cloudsql/PROJECT:REGION:INSTANCE_NAME" | \
gcloud secrets create boards-database-url --data-file=-

3. Mount Config Files

Create configs as secrets:

# Create generators config secret
gcloud secrets create boards-generators-config \
--data-file=config/generators.yaml

# Create storage config secret
gcloud secrets create boards-storage-config \
--data-file=config/storage_config.yaml

Mount as volumes:

gcloud run services update boards-api \
--region us-central1 \
--update-secrets="/app/config/generators.yaml=boards-generators-config:latest" \
--update-secrets="/app/config/storage_config.yaml=boards-storage-config:latest" \
--set-env-vars "BOARDS_GENERATORS_CONFIG_PATH=/app/config/generators.yaml,BOARDS_STORAGE_CONFIG_PATH=/app/config/storage_config.yaml"

Deploy Worker Service

The worker needs to run continuously, so configure minimum instances:

gcloud run deploy boards-worker \
--image ghcr.io/weirdfingers/boards-backend:latest \
--platform managed \
--region us-central1 \
--no-allow-unauthenticated \
--cpu 2 \
--memory 1Gi \
--min-instances 1 \
--max-instances 5 \
--command "boards-worker" \
--args "--log-level,info,--processes,1,--threads,1" \
--set-env-vars "BOARDS_LOG_FORMAT=json,BOARDS_INTERNAL_API_URL=https://boards-api-xxxxx-uc.a.run.app" \
--set-secrets "BOARDS_DATABASE_URL=boards-database-url:latest,BOARDS_REDIS_URL=boards-redis-url:latest,BOARDS_GENERATOR_API_KEYS=boards-api-keys:latest" \
--add-cloudsql-instances PROJECT:REGION:INSTANCE_NAME
note

Workers need --min-instances 1 to ensure jobs are processed. Cloud Run Jobs is an alternative for batch processing.

Redis Options

Option 1: Memorystore (VPC Required)

Create a Memorystore instance:

gcloud redis instances create boards-redis \
--size=1 \
--region=us-central1 \
--redis-version=redis_7_0

Connect Cloud Run to VPC:

# Create VPC connector
gcloud compute networks vpc-access connectors create boards-connector \
--region us-central1 \
--subnet default

# Update services to use connector
gcloud run services update boards-api \
--vpc-connector boards-connector \
--region us-central1

Option 2: Upstash Redis (Simpler)

Upstash provides serverless Redis accessible without VPC:

  1. Create a Redis database at upstash.com
  2. Use the provided connection URL:
echo -n "rediss://default:xxxxx@us1-xxxxx.upstash.io:6379" | \
gcloud secrets create boards-redis-url --data-file=-

Secret Manager Setup

Store all sensitive values in Secret Manager:

# Database URL
echo -n "postgresql://..." | gcloud secrets create boards-database-url --data-file=-

# Redis URL
echo -n "redis://..." | gcloud secrets create boards-redis-url --data-file=-

# Generator API keys
echo -n '{"fal": "key", "openai": "key"}' | gcloud secrets create boards-api-keys --data-file=-

# Grant access to Cloud Run service account
gcloud secrets add-iam-policy-binding boards-database-url \
--member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"

Deploy Frontend

Build and deploy the frontend:

# Build frontend image
gcloud builds submit \
--tag gcr.io/PROJECT/boards-frontend \
--build-arg NEXT_PUBLIC_API_URL=https://boards-api-xxxxx-uc.a.run.app \
--build-arg NEXT_PUBLIC_GRAPHQL_URL=https://boards-api-xxxxx-uc.a.run.app/graphql

# Deploy to Cloud Run
gcloud run deploy boards-frontend \
--image gcr.io/PROJECT/boards-frontend \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--port 3000

Custom Domain

Map a custom domain:

gcloud run domain-mappings create \
--service boards-api \
--domain api.boards.example.com \
--region us-central1

Follow the DNS verification instructions provided.

CI/CD with Cloud Build

Create cloudbuild.yaml:

steps:
# Deploy API
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args:
- 'run'
- 'deploy'
- 'boards-api'
- '--image'
- 'ghcr.io/weirdfingers/boards-backend:${_VERSION}'
- '--region'
- 'us-central1'

# Deploy Worker
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args:
- 'run'
- 'deploy'
- 'boards-worker'
- '--image'
- 'ghcr.io/weirdfingers/boards-backend:${_VERSION}'
- '--region'
- 'us-central1'

substitutions:
_VERSION: latest

Trigger on new releases:

gcloud builds triggers create github \
--repo-name=your-repo \
--branch-pattern="^main$" \
--build-config=cloudbuild.yaml

Monitoring

Cloud Logging

View logs:

gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=boards-api" --limit 100

Cloud Monitoring

Set up alerts:

gcloud alpha monitoring policies create \
--notification-channels=CHANNEL_ID \
--display-name="Boards API Errors" \
--condition-display-name="Error rate > 5%" \
--condition-filter='resource.type="cloud_run_revision" AND metric.type="run.googleapis.com/request_count" AND metric.labels.response_code_class="5xx"'

Cost Optimization

  • API: Set --min-instances 0 to scale to zero when idle
  • Worker: Consider Cloud Run Jobs for batch processing
  • Redis: Use Upstash pay-per-request for low traffic

Troubleshooting

Cold Start Latency

If cold starts are too slow:

gcloud run services update boards-api \
--min-instances 1 \
--region us-central1

Cloud SQL Connection Errors

  1. Verify Cloud SQL instance name is correct
  2. Check service account has cloudsql.client role
  3. Ensure Cloud SQL Admin API is enabled

Worker Not Processing Jobs

  1. Verify --min-instances 1 for worker
  2. Check Redis connectivity
  3. Review worker logs in Cloud Logging

Next Steps