Article 25 – Google Cloud Run

Here is the question that sparked Cloud Run’s existence: “Can we have the scale-to-zero simplicity of serverless, but with the freedom to run any Docker container?”

For years the answer was “pick one.” App Engine Standard gives you serverless but restricts your runtime. GKE gives you container freedom but requires managing a cluster. Cloud Run gives you both.

What Exactly Is Cloud Run?

Cloud Run is a fully managed, serverless platform for running stateless containers. Let me break that down:

  • From Serverless — Automatic scaling (including to zero), no infrastructure to manage, pay-per-use billing
  • From Containers — Package your app in a standard Docker container. Any language, any library, any binary

The workflow is dead simple. You build a container image, push it to Artifact Registry, and tell Cloud Run to deploy it.

# Deploy a container to Cloud Run
gcloud run deploy my-api \
    --image us-central1-docker.pkg.dev/my-project/my-repo/my-app:v1 \
    --platform managed \
    --region us-central1 \
    --allow-unauthenticated

Output:

Deploying container to Cloud Run service [my-api] in project [my-project] region [us-central1]
✓ Deploying new service... Done.
  ✓ Creating Revision...
  ✓ Routing traffic...
Done.
Service [my-api] revision [my-api-00001-xyz] has been deployed and is serving 100 percent of traffic.
Service URL: https://my-api-abc123-uc.a.run.app

Cloud Run gives you a secure HTTPS endpoint and handles everything else.

Revisions and Traffic Splitting

Every time you deploy a new version or change the configuration, Cloud Run creates a new immutable Revision. Just like App Engine, you can split traffic between revisions for safe rollouts:

# Send 90% to current, 10% to new revision for canary testing
gcloud run services update-traffic my-api \
    --to-revisions=my-api-00001-xyz=90,my-api-00002-abc=10
Container Concurrency — Why Cloud Run Is Cost-Effective

Here is a key difference from Cloud Functions. A Cloud Function instance handles one request at a time. A Cloud Run container instance can handle multiple requests simultaneously — up to 80 concurrent requests by default.

If you have 80 simultaneous users hitting your API, you might need only one Cloud Run instance. With Cloud Functions, you would need 80 instances. That makes Cloud Run dramatically more cost-effective for services with steady traffic.

CPU Allocation Modes

CPU during request processing (default) — The classic serverless model. When your container is idle, CPU is throttled. You only pay for active processing time. The service can scale to zero.

CPU always allocated — CPU stays available even between requests. Use this for background processing, websockets, or queue workers. Note: the service will NOT scale to zero in this mode.

Use the default mode unless you have a clear need for background work.

Controlling Access

Ingress settings:
All — Publicly accessible from the internet
Internal — Only reachable from within your VPC
Internal and Cloud Load Balancing — Internal but can also be a backend for an LB

Authentication: By default, Cloud Run services are private. Callers need the Cloud Run Invoker (roles/run.invoker) role. The --allow-unauthenticated flag grants this role to allUsers, making it truly public.

VPC Access — Reaching Private Resources

Cloud Run runs in a Google-managed environment, not in your VPC. If your container needs to connect to Cloud SQL, Memorystore, or other private resources, it needs a bridge.

That bridge is a Serverless VPC Access Connector. Create the connector in your VPC, configure your Cloud Run service to use it, and your container can reach private IPs.

When to Use What — The Compute Decision Guide
ServiceBest ForScales to Zero?
Compute EngineFull control, legacy apps, custom OSNo
GKE StandardComplex microservices, stateful appsNo
GKE AutopilotKubernetes without node managementYes (pods)
App Engine StandardWeb apps in supported runtimesYes
Cloud FunctionsEvent-driven, single-purpose codeYes
Cloud RunStateless web services in containersYes

The simple rule:
– Need a full VM? → Compute Engine
– Need Kubernetes? → GKE
– Simple web app in a supported language? → App Engine Standard
– Small code reacting to an event? → Cloud Functions
– Web service in a container with serverless scaling? → Cloud Run

Common Pitfalls and Best Practices

Pitfall: Running a stateful app (like a database) in Cloud Run. The container’s file system is ephemeral.
Best Practice: Design containers to be stateless. Externalize state to Cloud SQL, Firestore, or Memorystore.

Pitfall: Setting concurrency too high for CPU-intensive apps. One instance gets overwhelmed.
Best Practice: Tune concurrency. For CPU-heavy work, lower it (even to 1). For I/O-bound work, higher is fine.

Pitfall: Using “CPU always allocated” and not realizing it will not scale to zero.
Best Practice: Use the default “CPU during requests” mode unless you specifically need background processing.

Pitfall: Large container images causing slow cold starts.
Best Practice: Use multi-stage Docker builds and minimal base images (alpine, distroless) to keep images lean.

Quick Reference
# Deploy a service
gcloud run deploy [SERVICE] --image [IMAGE] --region [REGION]

# List services
gcloud run services list

# Describe a service
gcloud run services describe [SERVICE] --region [REGION]

# Make a service public
gcloud run services add-iam-policy-binding [SERVICE] \
    --member=allUsers --role=roles/run.invoker

# Make a service private
gcloud run services remove-iam-policy-binding [SERVICE] \
    --member=allUsers --role=roles/run.invoker

# Split traffic between revisions
gcloud run services update-traffic [SERVICE] \
    --to-revisions=REV1=90,REV2=10

# View logs
gcloud logging read "resource.type=cloud_run_revision \
    AND resource.labels.service_name=[SERVICE]"