Cloud Run is GCP’s fully managed serverless container platform. Deploy any container image and Cloud Run handles scaling, HTTPS, load balancing, and infrastructure — you pay only when requests are being processed. Cloud Run powers Cloud Functions 2nd gen under the hood and is the recommended choice for most containerized workloads that do not need Kubernetes complexity.

Cloud Run vs. Alternatives

Criteria Cloud Run Cloud Functions GKE Compute Engine
Deployment unit Container image Function code Kubernetes manifests VM image
Scaling 0 to N (automatic) 0 to N (automatic) HPA + Cluster Autoscaler MIG autoscaling
Max timeout 60 minutes 60 minutes (gen2) Unlimited Unlimited
Cold start ~1-3 seconds ~0.5-2 seconds Pod schedule time N/A (always on)
Networking VPC connector or direct VPC connector or direct Full VPC Full VPC
Best for APIs, microservices Event handlers Complex orchestration Legacy, full OS control

Deploy a Service

  # Build and push image to Artifact Registry
gcloud artifacts repositories create app \
  --repository-format=docker --location=us-central1

gcloud builds submit --tag us-central1-docker.pkg.dev/learning-gcp-dev/app/web:v1 .

# Deploy to Cloud Run
gcloud run deploy web-app \
  --image=us-central1-docker.pkg.dev/learning-gcp-dev/app/web:v1 \
  --region=us-central1 \
  --platform=managed \
  --allow-unauthenticated \
  --memory=512Mi \
  --cpu=1 \
  --min-instances=0 \
  --max-instances=10 \
  --concurrency=80 \
  --port=8080 \
  --set-env-vars=ENV=production \
  --service-account=web-app@learning-gcp-dev.iam.gserviceaccount.com
  

Sample Dockerfile

  FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]
  

Cloud Run requires the container to listen on the port specified by the PORT environment variable (default 8080).

Configuration Options

  gcloud run deploy web-app \
  --image=us-central1-docker.pkg.dev/learning-gcp-dev/app/web:v2 \
  --region=us-central1 \
  --memory=1Gi \
  --cpu=2 \
  --cpu-boost \
  --min-instances=1 \
  --max-instances=100 \
  --concurrency=100 \
  --timeout=300 \
  --set-secrets=DB_PASSWORD=db-password:latest,API_KEY=api-key:latest \
  --vpc-connector=projects/learning-gcp-dev/locations/us-central1/connectors/my-connector \
  --vpc-egress=private-ranges-only \
  --no-cpu-throttling \
  --session-affinity
  
Setting Purpose Production Tip
--min-instances=1 Eliminate cold starts Use for latency-critical APIs
--cpu-boost Extra CPU during startup Reduces cold start time
--concurrency Requests per instance Higher = fewer instances, more memory per request
--no-cpu-throttling CPU always allocated Background processing in same container
--session-affinity Sticky sessions WebSocket or stateful sessions

Cloud Run Jobs

For batch and scheduled workloads (not HTTP services):

  gcloud run jobs create data-export \
  --image=us-central1-docker.pkg.dev/learning-gcp-dev/app/exporter:v1 \
  --region=us-central1 \
  --memory=2Gi \
  --cpu=2 \
  --max-retries=3 \
  --task-timeout=3600 \
  --set-env-vars=EXPORT_BUCKET=learning-gcp-dev-exports

# Execute manually
gcloud run jobs execute data-export --region=us-central1

# Schedule with Cloud Scheduler
gcloud scheduler jobs create http nightly-export \
  --schedule="0 3 * * *" \
  --uri="https://us-central1-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/learning-gcp-dev/jobs/data-export:run" \
  --http-method=POST \
  --oauth-service-account-email=scheduler@learning-gcp-dev.iam.gserviceaccount.com
  

VPC and Private Connectivity

Access resources on private IPs (Cloud SQL, Memorystore, internal APIs):

  # Create VPC connector
gcloud compute networks vpc-access connectors create my-connector \
  --region=us-central1 \
  --network=learning-vpc \
  --range=10.8.0.0/28 \
  --min-instances=2 \
  --max-instances=10

# Deploy with VPC access
gcloud run deploy web-app \
  --vpc-connector=my-connector \
  --vpc-egress=private-ranges-only \
  --region=us-central1
  

For Cloud SQL, prefer the Cloud SQL connector library over VPC connector when possible — lower latency and no connector cost.

Custom Domains and TLS

  # Map custom domain
gcloud run domain-mappings create \
  --service=web-app \
  --domain=api.example.com \
  --region=us-central1

# Verify DNS records shown in output
gcloud run domain-mappings describe \
  --domain=api.example.com \
  --region=us-central1
  

Cloud Run automatically provisions and renews TLS certificates for mapped domains.

Traffic Management

  # Deploy new revision without routing traffic
gcloud run deploy web-app \
  --image=us-central1-docker.pkg.dev/learning-gcp-dev/app/web:v3 \
  --region=us-central1 \
  --no-traffic

# Split traffic (canary deployment)
gcloud run services update-traffic web-app \
  --region=us-central1 \
  --to-revisions=web-app-v2=90,web-app-v3=10

# Rollback
gcloud run services update-traffic web-app \
  --region=us-central1 \
  --to-revisions=web-app-v2=100
  

Real-World Scenario: API Platform

A startup runs 8 microservices on Cloud Run:

  Cloud Load Balancer (global)
  ├── api.example.com → Cloud Run: api-gateway
  ├── auth.example.com → Cloud Run: auth-service
  └── app.example.com → Cloud Run: web-frontend

Internal (VPC):
  ├── Cloud Run: order-service → Cloud SQL (private IP)
  ├── Cloud Run: payment-service → external API via VPC
  └── Cloud Run: notification-service → Pub/Sub
  
Service Min Instances Max Memory Monthly Cost
api-gateway 1 50 512Mi ~$30
auth-service 1 20 256Mi ~$15
order-service 0 100 1Gi ~$20 (spiky)
batch-export (Job) 0 2Gi ~$5

Total: ~$70/month vs. ~$400/month for equivalent GKE cluster.

Common Mistakes

Mistake Impact Fix
Container not listening on PORT Deployment fails health check Read $PORT env var
No min-instances on critical APIs Cold start latency (1-3s) Set --min-instances=1
Large container images Slow cold starts Multi-stage Docker builds, distroless images
No max-instances limit Runaway cost during attack Set --max-instances
Stateful data in container Lost on scale-down Use Cloud SQL, Firestore, GCS

Best Practices

  • Use Artifact Registry with vulnerability scanning enabled
  • Keep container images small (< 200MB) with multi-stage builds
  • Set resource limits (memory, CPU) based on load testing
  • Use Secret Manager for credentials, not environment variables in source
  • Implement health check endpoints (/health, /ready)
  • Use structured JSON logging for Cloud Logging integration
  • Configure IAM — avoid --allow-unauthenticated unless truly public
  • Use traffic splitting for canary deployments
  • Monitor with Cloud Monitoring request latency and error rate metrics

Troubleshooting

Container failed to start:

  gcloud run services describe web-app --region=us-central1 \
  --format="value(status.conditions)"
gcloud logging read 'resource.type="cloud_run_revision" severity>=ERROR' --limit=10
  

502 Bad Gateway: Container is not listening on the correct port or health check fails. Verify PORT env var and startup time vs. timeout.

Cold start too slow:

  gcloud run deploy web-app --cpu-boost --min-instances=1 --region=us-central1
# Reduce image size; use smaller base image (distroless, alpine)
  

Cannot reach Cloud SQL: Use Cloud SQL connector library or verify VPC connector and --vpc-egress settings.

Permission denied on deploy:

  gcloud projects add-iam-policy-binding learning-gcp-dev \
  --member="user:[email protected]" \
  --role="roles/run.developer"
  

Cloud Run is the sweet spot between serverless simplicity and container flexibility on GCP.

Next: Advanced Networking — hybrid connectivity and private service access.