Google Cloud Run skill for Codex

Private by default, scaled on purpose. Codex deploys to Cloud Run with a dedicated runtime service account, private ingress and pinned image digests, then sizes concurrency and instance limits against the real downstream connection budget. Rollouts go through tagged no-traffic revisions with a one-line rollback, and batch work moves to sharded, idempotent Cloud Run jobs.

  • Cloud Platforms
  • $gcp-cloud-run
  • 210 lines
  • 2 files
  • ~2,400 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- gcp-cloud-run

Installs to ~/.agents/skills/gcp-cloud-run/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $gcp-cloud-run, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Deploys and tunes Google Cloud Run services and jobs with gcloud - container contract, concurrency, min and max instances, CPU allocation, startup probes, Secret Manager env vars, dedicated runtime service accounts, run.invoker IAM, ingress, Direct VPC egress and tagged canary revisions. Use when shipping a container or source directory to Cloud Run, fixing cold starts or 503s, splitting traffic between revisions, running batch work as a Cloud Run job, or locking down who can invoke a service.

Use it when

  • A container or source folder needs to run on Cloud Run
  • Cold starts, 503s or a DB connection storm trace back to Cloud Run scaling
  • One service must call another privately with IAM
  • A revision rollout needs a canary and a quick rollback

Use something else when

  • You are writing a single event handler with the Functions Framework: use gcp-cloud-functions
  • The workload needs Kubernetes primitives such as DaemonSets or custom controllers: use gcp-gke
  • You only need to design service accounts and role bindings: use gcp-iam

How to check the result

The private URL returns 403 without a token and 200 with an identity token, the revision runs as the dedicated service account, and the traffic split matches the plan.

The mistake it prevents

Leaving max instances unbounded while each instance opens a DB pool, so a traffic spike exhausts the database's connection limit.

~/.agents/skills/gcp-cloud-run/SKILL.md

--- name: gcp-cloud-run description: Deploys and tunes Google Cloud Run services and jobs with gcloud - container contract, concurrency, min and max instances, CPU allocation, startup… license: MIT ---

Done well means: the service runs as its own least-privilege service account, is private unless the user said “public”, scales within a budget the user agreed to, gets secrets from Secret Manager rather than plain env vars, and every rollout can be shifted back to the previous revision in one command.

Before changing anything

  1. Read AGENTS.md for the project, region and deploy conventions (Makefile target, Cloud Build trigger, Terraform). If the service is managed by Terraform or a service.yaml, change it there, not with ad-hoc gcloud flags that the next apply will revert.
  2. Inspect what exists (needs network and gcloud auth; ask before running if the sandbox blocks network):
gcloud config list --format='value(core.project,run.region)'
gcloud run services list --region="$REGION"
gcloud run services describe api --region="$REGION" --format=export > current.yaml
gcloud run revisions list --service=api --region="$REGION" --limit=5

--format=export gives a YAML you can diff and re-apply with gcloud run services replace current.yaml. Keep it as the rollback reference.

Service or job?

WorkloadUseKey settings
HTTP/gRPC/WebSocket server, responds to requestsServiceconcurrency, min/max instances, timeout
Batch, migration, nightly export, queue drain that endsJobtasks, parallelism, max retries, task timeout
Pull-based consumer with no HTTP endpoint, always onWorker pool, or a service with instance-based billing and min instancescheck worker pool availability in the region
Event from Pub/Sub, Storage, EventarcService with an Eventarc trigger or push subscriptionauthenticated push, idempotent handler

Container contract

  • Listen on 0.0.0.0:$PORT (default 8080). Never hard-code the port.
  • Be stateless: local disk is an in-memory filesystem that counts against memory.
  • Handle SIGTERM: the instance gets about 10 seconds to finish before SIGKILL. Flush logs and close DB pools there.
  • Log JSON to stdout with a severity field; Cloud Logging parses it into structured logs.
  • Build for linux/amd64 unless the user explicitly targets otherwise; an arm64 image built on an Apple Silicon laptop fails to start with an exec format error.

Deploy

# From source (Cloud Build + buildpacks or Dockerfile); needs Cloud Build and Artifact Registry APIs
gcloud run deploy api --source . --region="$REGION" \
  --service-account=api-runtime@"$PROJECT".iam.gserviceaccount.com \
  --no-allow-unauthenticated \
  --set-secrets=DATABASE_URL=db-url:latest \
  --set-env-vars=LOG_LEVEL=info

# From a prebuilt image (preferred in CI: build once, deploy the same digest everywhere)
gcloud run deploy api --region="$REGION" \
  --image="$REGION-docker.pkg.dev/$PROJECT/apps/api@sha256:<digest>"
  • Pin images by digest in production deploys, not :latest.
  • --set-env-vars replaces the whole set; --update-env-vars merges. Same split for --set-secrets / --update-secrets. Using --set-* by accident wipes existing values.
  • Pin secret versions (db-url:3) for reproducible rollbacks; :latest is resolved when an instance starts, so long-lived instances keep the old value until replaced.

Production deploys are outward-facing. Show the user the exact command, the target project and region, and get confirmation before running it.

Scaling and cost decisions

Symptom or needSetting
Cold starts hurt p99 latency--min-instances=1 (billed while idle) plus --cpu-boost
Handler is CPU-bound or not thread-safeLower --concurrency (default 80; 1 for single-threaded runtimes doing heavy work)
Background work after the response returns--no-cpu-throttling (instance-based billing); otherwise CPU is throttled outside requests
Downstream DB has a connection cap--max-instances x pool size per instance must stay below the DB limit
Long requests (exports, streaming)--timeout up to 3600s; consider a job instead
Spiky traffic, cost matters mostDefault request-based billing, min instances 0

Rules of thumb:

  • Start with --cpu=1 --memory=512Mi, then size from Cloud Monitoring (run.googleapis.com/container/memory/utilizations, CPU utilisation, instance count).
  • Always set --max-instances on anything that talks to a database or a paid API. Unbounded scale-out turns a traffic spike into a connection storm or a bill.
  • Memory above 4 GiB needs at least 2 vCPU; the CLI rejects invalid pairs.

Startup and health

Add a startup probe when boot takes more than a few seconds, so traffic waits for readiness. Probes are set in the service YAML:

spec:
  template:
    spec:
      containers:
        - image: REGION-docker.pkg.dev/PROJECT/apps/api@sha256:...
          startupProbe:
            httpGet: { path: /healthz/ready }
            periodSeconds: 2
            failureThreshold: 30
          livenessProbe:
            httpGet: { path: /healthz/live }
            periodSeconds: 15

Liveness must check only the process itself, never the database; a DB blip should not restart every instance at once.

Access control

Who calls itConfiguration
Public website or API--allow-unauthenticated (only with explicit user approval)
Another Cloud Run service or GCP workloadPrivate; grant the caller’s SA roles/run.invoker; caller sends an ID token with the service URL as audience
Browser users of an internal appPrivate behind IAP or an external load balancer, not allUsers
Pub/Sub push, Cloud Scheduler, EventarcTheir SA gets roles/run.invoker; enable OIDC token on the push/job
gcloud run services add-iam-policy-binding api --region="$REGION" \
  --member=serviceAccount:worker@"$PROJECT".iam.gserviceaccount.com \
  --role=roles/run.invoker

Network restriction is separate from IAM. --ingress=internal blocks the internet entirely; --ingress=internal-and-cloud-load-balancing allows only your load balancer. For private egress to Cloud SQL private IP, Memorystore or on-prem, use Direct VPC egress: --network=default --subnet=run-subnet --vpc-egress=private-ranges-only.

Calling a private service from code (Node):

import { GoogleAuth } from "google-auth-library";
const auth = new GoogleAuth();
const client = await auth.getIdTokenClient(process.env.TARGET_URL); // audience = service URL
const res = await client.request({ url: `${process.env.TARGET_URL}/v1/jobs`, method: "POST", data });

Safe rollouts with tags

gcloud run deploy api --image="$IMAGE" --region="$REGION" --no-traffic --tag=canary
# smoke test https://canary---api-<hash>-<region>.a.run.app
gcloud run services update-traffic api --region="$REGION" --to-tags=canary=10
gcloud run services update-traffic api --region="$REGION" --to-latest        # promote
gcloud run services update-traffic api --region="$REGION" --to-revisions=api-00041-abc=100  # roll back

Watch error rate and latency per revision in the Cloud Run metrics before each step.

Jobs

gcloud run jobs deploy reindex --image="$IMAGE" --region="$REGION" \
  --tasks=20 --parallelism=5 --max-retries=2 --task-timeout=30m \
  --service-account=reindex-runtime@"$PROJECT".iam.gserviceaccount.com
gcloud run jobs execute reindex --region="$REGION" --wait
  • Each task reads CLOUD_RUN_TASK_INDEX and CLOUD_RUN_TASK_COUNT to pick its shard. Tasks must be idempotent because retries rerun the whole task.
  • Schedule with Cloud Scheduler calling the Jobs API, with a dedicated SA holding roles/run.invoker on the job.
  • A job that mutates production data (migrations, backfills) needs user confirmation before execute.

Gotchas

  • Default compute service account has Editor on the project in many orgs. Never ship with it; create one SA per service.
  • 503 with “no available instance” usually means max instances reached or the container crashed on start. Read gcloud logging read 'resource.type="cloud_run_revision"' --limit=50.
  • Request-based billing throttles CPU after the response: fire-and-forget promises and background threads stall. Either await the work or switch billing mode.
  • Global variables persist across requests on one instance; never keep per-user state there.
  • WebSockets and streaming count against --timeout; clients must reconnect.
  • A revision is immutable. Changing an env var creates a new revision and rolls traffic unless the service pins traffic to specific revisions.

Verification

gcloud run services describe api --region="$REGION" \
  --format='value(status.url,status.latestReadyRevisionName,spec.template.spec.serviceAccountName)'
curl -sS -o /dev/null -w '%{http_code}\n' "$URL/healthz/ready"     # 403 expected if private
curl -sS -H "Authorization: Bearer $(gcloud auth print-identity-token)" "$URL/healthz/ready"
gcloud run services get-iam-policy api --region="$REGION"
  • Private services return 403 without a token and 200 with one.
  • The runtime SA is the dedicated one, not [email protected].
  • Traffic split shows the intended percentages; the previous revision is still listed.

Report template

**Service:** api (europe-west1), revision api-00042-xyz, image @sha256:ab12...
**Access:** private; run.invoker for worker@ and scheduler@; ingress internal-and-cloud-load-balancing
**Scaling:** min 1 / max 20, concurrency 40, 1 vCPU / 1Gi, cpu-boost on
**Secrets:** DATABASE_URL -> db-url:3
**Traffic:** canary 10% -> 100% after 30 min, error rate flat
**Rollback:** gcloud run services update-traffic api --to-revisions=api-00041-abc=100

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Google Cloud Run"
  short_description: "Private by default, scaled on purpose"
  default_prompt: "$gcp-cloud-run Deploy this Dockerfile to Cloud Run in europe-west1 as a private service"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem