Cloud Run Deployment
Deploy containerized applications to Cloud Run - Google's serverless container platform with automatic scaling.
What you'll accomplish
Your containerized application running on Cloud Run with a public URL
Prerequisites
- GCP account with billing enabled
- gcloud CLI installed and configured
- Docker installed locally
- A containerized application
What is Cloud Run?
Cloud Run is a fully managed platform for running containers. You give it a Docker image, and it handles everything else:
- Automatic scaling - From zero to thousands of instances
- Pay per use - Only pay when your code runs
- HTTPS included - Free SSL certificates
- Global load balancing - Built-in for all services
Your Container Image
↓
Cloud Run
↓
Scales automatically (0 → N instances)
↓
Global CDN + Load Balancer
↓
Users worldwide
Cloud Run vs Other GCP Services
| Service | Best For | Complexity |
|---|---|---|
| Cloud Run | Web apps, APIs, microservices | Low |
| Cloud Functions | Event-driven single functions | Low |
| GKE | Kubernetes workloads | High |
| Compute Engine | Full server control | Medium |
Note
Cloud Run is the recommended path for most web applications. Start here, and only consider alternatives if you have specific requirements Cloud Run can't meet.
Prerequisites
Before deploying, ensure you have:
- Docker image - Your app containerized
- Dockerfile - Build instructions
- Port configuration - App listens on a specific port
Sample Dockerfile (Node.js)
dockerfileFROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . # Cloud Run uses PORT environment variable ENV PORT=8080 EXPOSE 8080 CMD ["node", "server.js"]
Cloud Run Requirements
- Container must listen on the
PORTenvironment variable - Container must be stateless (no persistent filesystem)
- Must respond to HTTP requests
- Maximum request timeout: 60 minutes (Pro)
Step 1: Enable Required APIs
bashgcloud services enable \ run.googleapis.com \ artifactregistry.googleapis.com \ cloudbuild.googleapis.com
Step 2: Create Artifact Registry Repository
Artifact Registry is Google's container registry (replacing the older Container Registry).
bash# Create repository gcloud artifacts repositories create docker-repo \ --repository-format=docker \ --location=us-central1 \ --description="Docker images for my apps" # Configure Docker to use Artifact Registry gcloud auth configure-docker us-central1-docker.pkg.dev
Step 3: Build and Push Your Image
Option A: Build with Cloud Build (Recommended)
Cloud Build builds your image in GCP's cloud - no local Docker required.
bash# Build and push with a single command gcloud builds submit \ --tag us-central1-docker.pkg.dev/PROJECT_ID/docker-repo/myapp:latest # Replace PROJECT_ID with your actual project ID
Option B: Build Locally and Push
bash# Build locally docker build -t myapp . # Tag for Artifact Registry docker tag myapp us-central1-docker.pkg.dev/PROJECT_ID/docker-repo/myapp:latest # Push to registry docker push us-central1-docker.pkg.dev/PROJECT_ID/docker-repo/myapp:latest
Option C: Use Container Registry (Legacy)
bash# Configure Docker for GCR gcloud auth configure-docker # Build and push gcloud builds submit --tag gcr.io/PROJECT_ID/myapp:latest
Step 4: Deploy to Cloud Run
Basic Deployment
bashgcloud run deploy myapp \ --image us-central1-docker.pkg.dev/PROJECT_ID/docker-repo/myapp:latest \ --platform managed \ --region us-central1 \ --allow-unauthenticated
Deployment with Configuration
bashgcloud run deploy myapp \ --image us-central1-docker.pkg.dev/PROJECT_ID/docker-repo/myapp:latest \ --platform managed \ --region us-central1 \ --allow-unauthenticated \ --port 8080 \ --memory 512Mi \ --cpu 1 \ --min-instances 0 \ --max-instances 10 \ --timeout 300 \ --concurrency 80 \ --set-env-vars "NODE_ENV=production"
Deployment Options Explained
| Option | Description | Default |
|---|---|---|
--port | Container port | 8080 |
--memory | Memory per instance | 256Mi |
--cpu | vCPUs per instance | 1 |
--min-instances | Minimum running instances | 0 |
--max-instances | Maximum instances | 100 |
--timeout | Request timeout (seconds) | 300 |
--concurrency | Requests per instance | 80 |
Step 5: Verify Deployment
bash# Get the service URL gcloud run services describe myapp \ --region us-central1 \ --format='value(status.url)' # Output: https://myapp-abc123xyz-uc.a.run.app # Test your service curl https://myapp-abc123xyz-uc.a.run.app/api/health
Environment Variables
Set at Deploy Time
bashgcloud run deploy myapp \ --image us-central1-docker.pkg.dev/PROJECT_ID/docker-repo/myapp:latest \ --set-env-vars "NODE_ENV=production,LOG_LEVEL=info"
Update Existing Service
bash# Add/update variables gcloud run services update myapp \ --region us-central1 \ --set-env-vars "KEY1=value1,KEY2=value2" # Update single variable gcloud run services update myapp \ --region us-central1 \ --update-env-vars "KEY1=newvalue" # Remove variable gcloud run services update myapp \ --region us-central1 \ --remove-env-vars "KEY1"
Secret Manager Integration
For sensitive values, use Secret Manager.
Create a Secret
bash# Create secret from value echo -n "my-database-password" | \ gcloud secrets create DB_PASSWORD --data-file=- # Create secret from file gcloud secrets create API_KEY --data-file=./api-key.txt
Use Secret in Cloud Run
bash# Mount as environment variable gcloud run services update myapp \ --region us-central1 \ --set-secrets "DB_PASSWORD=DB_PASSWORD:latest" # Mount as file gcloud run services update myapp \ --region us-central1 \ --set-secrets "/secrets/api-key=API_KEY:latest"
Grant Access
Cloud Run's service account needs permission:
bash# Get the service account SERVICE_ACCOUNT=$(gcloud run services describe myapp \ --region us-central1 \ --format='value(spec.template.spec.serviceAccountName)') # Grant access to secret gcloud secrets add-iam-policy-binding DB_PASSWORD \ --member="serviceAccount:$SERVICE_ACCOUNT" \ --role="roles/secretmanager.secretAccessor"
Traffic Splitting and Revisions
Every deployment creates a new revision. You can split traffic between revisions.
View Revisions
bashgcloud run revisions list --service myapp --region us-central1
Split Traffic
bash# 90% to latest, 10% to previous revision gcloud run services update-traffic myapp \ --region us-central1 \ --to-revisions myapp-00005-abc=90,myapp-00004-xyz=10 # Send all traffic to latest gcloud run services update-traffic myapp \ --region us-central1 \ --to-latest
Gradual Rollout
bash# Deploy new version with 0% traffic gcloud run deploy myapp \ --image us-central1-docker.pkg.dev/PROJECT_ID/docker-repo/myapp:v2 \ --no-traffic # Gradually increase traffic gcloud run services update-traffic myapp --to-revisions LATEST=10 gcloud run services update-traffic myapp --to-revisions LATEST=50 gcloud run services update-traffic myapp --to-revisions LATEST=100
Service Configuration
CPU Allocation
bash# Always-on CPU (recommended for production) gcloud run services update myapp \ --cpu-throttling=false # CPU only during requests (cheaper, may have cold starts) gcloud run services update myapp \ --cpu-throttling=true
Memory and CPU Options
| Memory | CPU Options |
|---|---|
| 128Mi-512Mi | 1 |
| 512Mi-1Gi | 1-2 |
| 1Gi-2Gi | 1-4 |
| 2Gi-4Gi | 2-4 |
| 4Gi-8Gi | 2-8 |
| 8Gi-32Gi | 4-8 |
Scaling Configuration
bashgcloud run services update myapp \ --min-instances 1 \ # Avoid cold starts --max-instances 100 \ # Limit scaling --concurrency 80 # Requests per instance
Connecting to Databases
Cloud SQL
bash# Enable Cloud SQL connection gcloud run services update myapp \ --add-cloudsql-instances PROJECT_ID:us-central1:my-database \ --set-env-vars "DB_HOST=/cloudsql/PROJECT_ID:us-central1:my-database"
VPC Connector (Private Networks)
bash# Create VPC connector gcloud compute networks vpc-access connectors create my-connector \ --region us-central1 \ --network default \ --range 10.8.0.0/28 # Use connector gcloud run services update myapp \ --vpc-connector my-connector \ --vpc-egress private-ranges-only
Authentication and Access Control
Public Access
bashgcloud run services update myapp \ --allow-unauthenticated
Authenticated Only
bashgcloud run services update myapp \ --no-allow-unauthenticated
Grant Access to Specific Users
bashgcloud run services add-iam-policy-binding myapp \ --region us-central1 \ --member="user:[email protected]" \ --role="roles/run.invoker"
Logging and Monitoring
View Logs
bash# Stream logs gcloud run services logs tail myapp --region us-central1 # Read recent logs gcloud run services logs read myapp --region us-central1 --limit 100 # In Cloud Console: Logging → Logs Explorer
Log Query
resource.type="cloud_run_revision"
resource.labels.service_name="myapp"
severity>=ERROR
Monitoring
- Go to Cloud Console → Monitoring
- View pre-built dashboards for Cloud Run
- See request count, latency, error rate
CI/CD with Cloud Build
cloudbuild.yaml
yamlsteps: # Build the container - name: 'gcr.io/cloud-builders/docker' args: - 'build' - '-t' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' - '-t' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:latest' - '.' # Push to Artifact Registry - name: 'gcr.io/cloud-builders/docker' args: - 'push' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' # Deploy to Cloud Run - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' entrypoint: gcloud args: - 'run' - 'deploy' - 'myapp' - '--image' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' - '--region' - 'us-central1' - '--platform' - 'managed' images: - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:latest'
Set Up Trigger
bash# Create trigger from GitHub gcloud builds triggers create github \ --repo-name=my-repo \ --repo-owner=my-github-user \ --branch-pattern="^main$" \ --build-config=cloudbuild.yaml
Cost Optimization
Free Tier
Cloud Run's free tier includes:
- 2 million requests/month
- 360,000 GB-seconds of memory
- 180,000 vCPU-seconds
Tips to Reduce Costs
- Use min-instances: 0 - Scale to zero when not in use
- Right-size memory - Don't over-provision
- Use CPU throttling - For non-latency-sensitive apps
- Set max-instances - Prevent runaway scaling
Estimate Costs
bash# View pricing gcloud run services describe myapp \ --region us-central1 \ --format="yaml(spec.template.spec.containers[0].resources)"
Troubleshooting
Container Fails to Start
bash# Check logs for errors gcloud run services logs read myapp --region us-central1 --limit 50 # Common causes: # - Wrong PORT (must use $PORT env var) # - Missing dependencies # - Permission issues
Cold Start Issues
bash# Set minimum instances gcloud run services update myapp \ --min-instances 1 # Use CPU boost at startup gcloud run services update myapp \ --cpu-boost
Request Timeouts
bash# Increase timeout (max 3600s for HTTP, 60min for gRPC) gcloud run services update myapp \ --timeout 600
Memory Errors
bash# Increase memory gcloud run services update myapp \ --memory 1Gi
Note
Checkpoint: Deploy your container to Cloud Run and verify it's accessible at the generated URL.