Google Cloud Run skill for Claude Code
Containers tuned for concurrency, cost and calm rollouts. Claude prepares containers that honor the Cloud Run contract, then picks concurrency, CPU allocation and instance limits from how the app actually behaves instead of defaults. Services run as dedicated service accounts with Secret Manager secrets, new revisions deploy with no traffic behind a tag, and traffic shifts in steps with an exact rollback command ready.
- name
cloud-run - size161 lines · ≈2,100 tokens
- files1
- licenseMIT
- updated
What Claude sees before loading it
This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.
description: Deploys and tunes Google Cloud Run services and jobs - container contract ($PORT, SIGTERM, stateless), concurrency and CPU allocation, min and max instances, startup CPU boost and cold starts, Secret Manager secrets, service accounts and IAM invoker access, ingress and VPC egress, revisions with tagged canary traffic splits, jobs for batch work, and logs. Use when containerizing an app for Cloud Run, choosing concurrency or instance settings, fixing cold starts, timeouts or 503s, wiring secrets, or rolling out a new revision safely. Try asking
- Deploy this Node app to Cloud Run with secrets and a canary rollout
- Our Cloud Run service has slow first requests and random 503s
- Background work stops after the response on Cloud Run. Why?
Use it when
- Containerizing and deploying a service or job to Cloud Run
- Choosing concurrency, CPU allocation or instance limits
- Cold starts, 503s, timeouts or memory errors on Cloud Run
- Wiring Secret Manager, service accounts or VPC egress
Skip it when
- You are optimizing the Dockerfile image itself — use dockerfile-optimization
- The workload runs on Kubernetes you manage — use kubernetes-deploy
How to check the result
The tagged canary URL passes health checks, canary metrics match the previous revision, and describe shows the dedicated service account, max instances and intended secrets.
The common mistake it prevents
Running background work after the response with request-based CPU, where Cloud Run throttles the instance and the work silently stalls.
A healthy Cloud Run service starts in seconds, listens on $PORT, holds no local state,
runs as a dedicated least-privilege service account, pulls secrets from Secret Manager,
has concurrency and instance limits chosen from measured load, and ships new revisions
with tagged previews and gradual traffic shifts.
Workflow
- [ ] 1. Read current config: gcloud run services describe SERVICE --region R --format export
- [ ] 2. Verify the container contract (port, startup time, SIGTERM, statelessness)
- [ ] 3. Choose concurrency, CPU/memory, CPU allocation mode, min/max instances
- [ ] 4. Identity: dedicated service account, invoker IAM, ingress
- [ ] 5. Secrets and config; VPC egress if private resources are needed
- [ ] 6. Deploy a tagged no-traffic revision; test; shift traffic gradually
- [ ] 7. Check logs, latency, instance count, and cost
2. Container contract
- Listen on
0.0.0.0:$PORT(default 8080); never hardcode. - Start fast: the default startup probe is a TCP check on the port; heavy init (model loading, migrations) delays readiness and cold starts. Run migrations as a Cloud Run job, not at service startup.
- On scale-in or deploy, the instance gets SIGTERM with 10 seconds before SIGKILL: stop accepting work, finish in-flight requests, flush logs.
- Filesystem is in-memory and counts against memory; use Cloud Storage, a database, or mounted volumes (Cloud Storage FUSE, NFS) for durable data.
- Log JSON to stdout; Cloud Logging parses
severity,message, andlogging.googleapis.com/tracefields for correlation.
3. Capacity settings
| Setting | Guidance |
|---|---|
--concurrency | Default 80, max 1000. Set to what one instance handles without latency rising: I/O-bound async apps 80-250; CPU-heavy or single-threaded runtimes 1-10 |
--cpu, --memory | Start 1 vCPU / 512Mi-1Gi; more than 1 vCPU only helps multi-threaded runtimes |
| CPU allocation | Request-based (default): CPU only during requests, background work is throttled. Instance-based (--no-cpu-throttling): CPU always on, needed for background threads, queues consumers, websockets housekeeping; billed for instance lifetime |
--min-instances | 1+ for latency-sensitive services (idle min instances are billed at a lower rate) |
--max-instances | Always set: protects databases and budgets from runaway scaling |
--cpu-boost | Startup CPU boost; cheap win for JVM/Node/Python cold starts |
--timeout | Request timeout up to 3600 s; streaming and websockets are bounded by it |
Database connections: max instances x per-instance pool size must stay below the database limit. Use Cloud SQL connectors or a pooler, and size pools to concurrency.
4. Identity and access
gcloud iam service-accounts create orders-api --display-name "orders-api runtime"
gcloud projects add-iam-policy-binding $PROJECT \
--member serviceAccount:orders-api@$PROJECT.iam.gserviceaccount.com --role roles/cloudsql.client
- Every service runs as its own service account (
--service-account), never the default Compute Engine account (it often has Editor). - Private services:
--no-allow-unauthenticatedand grantroles/run.invokerto calling service accounts; callers send an ID token with the service URL as audience. - Public services:
--allow-unauthenticated(subject to org policy) and authenticate users in the app or via IAP / a load balancer. --ingress internalorinternal-and-cloud-load-balancingto block direct internet access when fronted by a load balancer with Cloud Armor.
5. Secrets, config, networking
gcloud secrets create db-password --replication-policy automatic
printf '%s' "$PASSWORD" | gcloud secrets versions add db-password --data-file=-
gcloud secrets add-iam-policy-binding db-password \
--member serviceAccount:orders-api@$PROJECT.iam.gserviceaccount.com --role roles/secretmanager.secretAccessor
- Expose as env var (
--set-secrets DB_PASSWORD=db-password:latest) or mounted file (--set-secrets /secrets/db=db-password:latest). Env vars resolve at instance start; mountedlatestfiles pick up new versions on read. Pin versions (:3) for reproducible rollbacks. - Plain config via
--set-env-varsor--env-vars-file env.yaml;--update-env-varschanges some without replacing all. - Private resources (Memorystore, private Cloud SQL IP, internal services): Direct VPC
egress (
--network,--subnet,--vpc-egress private-ranges-only) is the default choice over Serverless VPC Access connectors.
6. Deploy and roll out
# Build from source with Cloud Build + buildpacks/Dockerfile, deploy without traffic
gcloud run deploy orders-api --source . --region europe-west1 \
--service-account orders-api@$PROJECT.iam.gserviceaccount.com \
--no-allow-unauthenticated --concurrency 80 --cpu 1 --memory 1Gi \
--min-instances 1 --max-instances 20 --cpu-boost \
--set-secrets DB_PASSWORD=db-password:latest \
--no-traffic --tag canary
# Test the tagged URL: https://canary---orders-api-<hash>-ew.a.run.app
gcloud run services update-traffic orders-api --region europe-west1 --to-tags canary=10
gcloud run services update-traffic orders-api --region europe-west1 --to-latest # promote
gcloud run services update-traffic orders-api --region europe-west1 --to-revisions orders-api-00041-abc=100 # rollback
For declarative config, keep service.yaml in the repo and apply with
gcloud run services replace service.yaml --region R. In CI, deploy prebuilt images
(--image REGION-docker.pkg.dev/PROJECT/REPO/orders-api:$SHA) built once and promoted
across environments; authenticate CI via Workload Identity Federation, not key files.
Production traffic changes need user confirmation.
Jobs
Batch and one-off work (migrations, exports, nightly processing) use Cloud Run jobs:
gcloud run jobs create migrate --image $IMAGE --region europe-west1 \
--command "npm" --args "run,migrate" --service-account orders-api@$PROJECT.iam.gserviceaccount.com \
--tasks 1 --max-retries 1 --task-timeout 15m
gcloud run jobs execute migrate --region europe-west1 --wait
Parallel jobs read CLOUD_RUN_TASK_INDEX and CLOUD_RUN_TASK_COUNT to shard work.
Schedule with Cloud Scheduler. Make tasks idempotent; retries rerun the whole task.
Diagnosing common problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Container failed to start and listen on port | Wrong port, binding to 127.0.0.1, slow startup | Bind 0.0.0.0:$PORT; reduce init; raise startup probe timeout |
| 503 / “no available instance” bursts | Max instances reached or slow scale-out with low concurrency | Raise max instances or concurrency; add min instances |
| High p99 on first requests | Cold starts | Min instances, CPU boost, lazy init, smaller image |
| Background tasks never finish | Request-based CPU throttles after response | Instance-based CPU, or move work to Cloud Tasks / Pub/Sub push |
| 504 after N seconds | Request timeout | Raise --timeout or make the work async |
| Memory limit exceeded | Writing to the in-memory filesystem, leaks | Stream to GCS, raise memory, fix leak |
gcloud run services logs read orders-api --region europe-west1 --limit 100
gcloud logging read 'resource.type="cloud_run_revision" AND resource.labels.service_name="orders-api" AND severity>=ERROR' --limit 50 --freshness 1h
gcloud run revisions list --service orders-api --region europe-west1
Gotchas
- Default service account with Editor role is a common privilege problem; replace it.
--set-env-varsreplaces all env vars; use--update-env-varsfor one change.- Ingress
internalblocks calls from Cloud Scheduler or other projects unless routed via VPC/LB. - Each deploy creates a revision; old revisions do not cost when not serving, but tags pin them for routing.
- Request-based billing still charges for min instances while idle.
Verification
curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" $TAG_URL/healthzreturns 200 on the canary tag before shifting traffic.- Metrics during canary: error rate, p95 latency, container instance count, and CPU/memory utilization match or beat the previous revision.
gcloud run services describeshows the dedicated service account, max instances, pinned or intended secret versions, and expected ingress.- A deliberate redeploy during load shows no failed requests (SIGTERM handled).
About this skill
Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.