Google Cloud
beginner
30-40 minutes

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

ServiceBest ForComplexity
Cloud RunWeb apps, APIs, microservicesLow
Cloud FunctionsEvent-driven single functionsLow
GKEKubernetes workloadsHigh
Compute EngineFull server controlMedium
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:

  1. Docker image - Your app containerized
  2. Dockerfile - Build instructions
  3. Port configuration - App listens on a specific port

Sample Dockerfile (Node.js)

dockerfile
FROM 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 PORT environment variable
  • Container must be stateless (no persistent filesystem)
  • Must respond to HTTP requests
  • Maximum request timeout: 60 minutes (Pro)

Step 1: Enable Required APIs

bash
gcloud 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

bash
gcloud 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

bash
gcloud 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

OptionDescriptionDefault
--portContainer port8080
--memoryMemory per instance256Mi
--cpuvCPUs per instance1
--min-instancesMinimum running instances0
--max-instancesMaximum instances100
--timeoutRequest timeout (seconds)300
--concurrencyRequests per instance80

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

bash
gcloud 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

bash
gcloud 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

MemoryCPU Options
128Mi-512Mi1
512Mi-1Gi1-2
1Gi-2Gi1-4
2Gi-4Gi2-4
4Gi-8Gi2-8
8Gi-32Gi4-8

Scaling Configuration

bash
gcloud 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

bash
gcloud run services update myapp \
  --allow-unauthenticated

Authenticated Only

bash
gcloud run services update myapp \
  --no-allow-unauthenticated

Grant Access to Specific Users

bash
gcloud 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

  1. Go to Cloud Console → Monitoring
  2. View pre-built dashboards for Cloud Run
  3. See request count, latency, error rate

CI/CD with Cloud Build

cloudbuild.yaml

yaml
steps:
  # 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

  1. Use min-instances: 0 - Scale to zero when not in use
  2. Right-size memory - Don't over-provision
  3. Use CPU throttling - For non-latency-sensitive apps
  4. 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.