Deploy a container on Cloud Run without meeting the startup error

Seven steps that remove the documented failure modes before they happen: make the container listen correctly, pick the right resource type, give it its own service account, grant the deployer actAs, reference secrets with the accessor role, deploy, then verify with the three calls that distinguish 429 from 503.

markdownguidev1DE0Q-7AK2stage: deploytrust: officialfamily: computeservice: cloud-runinterface: clicloud run deployPORT0.0.0.0--service-accountrevisionworker poolroles/iam.serviceAccountUserroles/secretmanager.secretAccessormaximum instances

Created 21 Oct 2026 · Modified 23 Oct 2026 · 1 version

Deploy a container on Cloud Run

Step 1 — make the container satisfy the contract

Three requirements, each of which has its own failure string if you miss it:

  1. listen on the value of the PORT environment variable, not a hard-coded port;
  2. bind 0.0.0.0, not 127.0.0.1;
  3. be a 64-bit Linux image, built on Linux (a Windows-built image with foreign layers fails at import).

Run it locally first. The documented first fix for the startup error is exactly that.

Step 2 — pick the resource type before you deploy anything

If the work isDeploy a
request-driven — a website, an API, a microserviceservice
a batch that runs to completionjob (which decomposes into executions and tasks)
a background consumer with no public endpointworker pool — it does not scale on requests
a single addressable long-lived containerinstance — no autoscaling

A background loop deployed as a service will not scale, because it receives no requests. That is the single most common mis-deploy here.

Step 3 — give it its own service account

Not the default. A purpose-made service account per service is what makes every later grant narrow, and it is what makes an audit log readable. See reference/identity/predefined-roles-and-permissions.md §4.

Step 4 — let the deployer act as it

The deployer needs roles/iam.serviceAccountUser on that service account (and roles/run.sourceDeveloper for a source deploy). Then pass it explicitly:

--service-account SERVICE_ACCOUNT

Relying on the default Compute Engine service account is how you meet "User does not have permission to access namespace" on a project where somebody has tidied it away.

Step 5 — secrets, with the right role

If the revision references a secret, the runtime service account needs roles/secretmanager.secretAccessor — the role carrying secretmanager.versions.access. roles/secretmanager.viewer is not enough and is the usual cause of a 503 at startup.

Step 6 — deploy, and expect a new revision

Every deploy creates an immutable revision carrying the images and the whole configuration. There is no "edit the env var" operation; there is only a new revision. The service's URL is stable across them, so rollback is picking an older revision.

Step 7 — verify, and read 429 and 503 as different problems

CallExpectedIf not
request the service URL200—
watch the logs on the first requestthe application's own stdoutif the container never logs, it never started: §1 of reference/compute/errors.md
load it past the instance ceiling429 "The request was aborted because there was no available instance."that is capacity — raise maximum instances
—503 "The request failed because the instance could not start successfully."that is configuration — missing secret, IAM, or network egress

Never answer a 503 by raising the instance ceiling, and never answer a 429 by changing the configuration. They look identical on a dashboard and they have opposite fixes.

For a migration

This is the cheapest landing place on Google Cloud for an already-containerised service: no cluster, no node pool, no scaling policy to port. The counterparts — AWS Fargate, App Runner and Lambda, Azure Container Apps and Container Instances — and what each of them needs that this does not, are in guides/parity/compute.md.

Read this document as markdown — the same bytes an agent receives.