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
gcloudCLI 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
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:
- Create a Redis database at upstash.com
- 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 0to 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
- Verify Cloud SQL instance name is correct
- Check service account has
cloudsql.clientrole - Ensure Cloud SQL Admin API is enabled
Worker Not Processing Jobs
- Verify
--min-instances 1for worker - Check Redis connectivity
- Review worker logs in Cloud Logging
Next Steps
- Cloud SQL Setup - Database configuration
- GCS Storage - Configure Cloud Storage
- Monitoring - Set up observability