GCP Cloud Run Config Analyzer

Read a Cloud Run service for the settings that change behaviour rather than limits: CPU throttled between requests, memory shared across eighty concurrent requests, a CPU and memory pairing that will not deploy, and the default service account.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Check the service. Nothing leaves this tab.

Wanted a different tool?

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

The default, which stops background work

No cpu-throttling annotation means CPU is throttled between requests, so anything after the response pauses until the next one arrives

{"metadata":{"name":"payments-api"},"spec":{"template":{"metadata":{"annotations":{}},"spec":{"containerConcurrency":80,"timeoutSeconds":300,"containers":[{"resources":{"limits":{"cpu":"1","memory":"512Mi"}}}]}}}}

Memory sized for one request

512 MiB across 80 concurrent requests is 6 MiB each, and the container is killed with a 503 whose logs show the kill rather than the cause

{"metadata":{"name":"api"},"spec":{"template":{"metadata":{"annotations":{"run.googleapis.com/cpu-throttling":"false"}},"spec":{"containerConcurrency":80,"timeoutSeconds":300,"serviceAccountName":"api@p.iam.gserviceaccount.com","containers":[{"resources":{"limits":{"cpu":"1","memory":"512Mi"}}}]}}}}

A pairing that will not deploy

8 GiB of memory requires at least 4 CPU. The rejection names the CPU, so the field people change is the wrong one.

{"metadata":{"name":"api"},"spec":{"template":{"metadata":{"annotations":{"run.googleapis.com/cpu-throttling":"false"}},"spec":{"containerConcurrency":1,"timeoutSeconds":300,"serviceAccountName":"api@p.iam.gserviceaccount.com","containers":[{"resources":{"limits":{"cpu":"1","memory":"8Gi"}}}]}}}}

Warm and frozen

min-instances keeps instances alive and CPU throttling means they cannot do anything while idle, so only the container start is saved

{"metadata":{"name":"api"},"spec":{"template":{"metadata":{"annotations":{"autoscaling.knative.dev/minScale":"2"}},"spec":{"containerConcurrency":8,"timeoutSeconds":300,"serviceAccountName":"api@p.iam.gserviceaccount.com","containers":[{"resources":{"limits":{"cpu":"1","memory":"2Gi"}}}]}}}}

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Doing work after the response is sent

    CPU is throttled to near zero between requests by default, so a background goroutine, a deferred write or an async log flush pauses mid-work and resumes only when the next request lands on that instance. Nothing fails and nothing is logged, so it completes under load and vanishes when traffic is quiet.

    Instead:Set CPU always allocated with --no-cpu-throttling, or finish everything before returning the response.

  2. Sizing memory from what one request needs

    Memory is per instance and the default concurrency is 80, so an instance at full load has eighty requests inside one limit. The container is killed with a 503 whose logs show the kill and not the cause.

    Instead:Divide the memory limit by the concurrency and check that number against one request, or lower concurrency until it fits.

  3. Changing the CPU when the deployment complains about the CPU

    Over 4 GiB of memory requires at least 2 CPU, over 8 GiB requires 4, over 24 GiB requires 8. The rejection names the CPU and the memory is what forced it, so the field people edit is the wrong one.

    Instead:Either raise the CPU to what the memory requires, or lower the memory below the threshold.

  4. Setting min-instances and leaving CPU throttled

    The warm instances have no CPU between requests, so they cannot refresh a cache, keep a connection alive or do anything the warmth was for. They only save the container start.

    Instead:Pair min-instances with CPU always allocated when the point is work between requests, and leave it throttled when the point is only start-up latency.

  5. Deploying without a service account

    The service then runs as the default Compute Engine account, which holds Editor on the project unless that grant was disabled, and is shared with every other workload that did not name one. An audit log entry cannot say which service acted.

    Instead:One service account per service, with only the roles it needs.

Between requests, your container has no CPU

This is the default, and it is not a performance setting. It changes what your code is able to do, and nothing about it appears in a log.

CPU throttling stops everything that happens after the response

Once the response is sent, the instance gets close to zero CPU until the next request lands on it, which may be minutes away or never. A background goroutine, a deferred write, an async log flush, a metrics exporter: all of them pause mid-work. Nothing fails and nothing is logged. The symptom is work that completes reliably under load and disappears when the service is quiet, which is the hardest shape of bug to reproduce.

gcloud run services update SERVICE --no-cpu-throttling

The annotation is run.googleapis.com/cpu-throttling.
"false" means CPU is always allocated, which reads
backwards and is worth checking twice.

Memory is per instance, and eighty requests share it

The default concurrency is 80. Memory and CPU are limits on the INSTANCE, not on the request, so an instance at full load has eighty requests inside one memory limit. Sizing memory from what a single request needs undersizes it by up to eighty times, and the container is killed and restarted with a 503 whose logs show the kill rather than the cause. This is the other half of every Cloud Run out-of-memory report.

512 MiB / 80 concurrent = 6 MiB per request

CPU and memory have to pair, and the error names the wrong one

Over 4 GiB needs at least 2 CPU, over 8 GiB needs 4, over 24 GiB needs 8. The deployment is rejected with a message about the CPU, so the field people change is the CPU when the memory is what forced it. Below 1 CPU is a different trade: the instance is sharing a fraction of a core across every concurrent request, so latency under load is scheduling rather than code.

min-instances with CPU throttled is warm and frozen

Keeping instances warm removes the container start. With CPU still throttled, those idle instances cannot refresh a cache, keep a connection alive or do anything else between requests. If the reason for min-instances was work between requests rather than start-up latency, the two settings have to go together.

The request timeout is only one of the timeouts

Cloud Run will hold a request for up to 60 minutes. Everything in front of it will not: a global load balancer has its own timeout, a browser gives up, a client library defaults to far less. So a long request is cancelled upstream while the instance keeps working and keeps being billed, and the retry starts a second copy of the same work. Match the timeouts along the whole path, or make the work asynchronous.

No service account means the default Compute Engine one

That account is granted Editor on the project unless the automatic grant was disabled, so the service can change almost anything. It is also shared with every other workload that did not name one, which means an audit log entry cannot say which service acted. One service account per service is the change worth making first.

What this cannot see

Whether CPU throttling matters to you, because that depends on what the container does after it responds, and nothing in the configuration says. It also cannot see whether the container listens on the PORT environment variable and on 0.0.0.0 rather than 127.0.0.1, which is the other silent Cloud Run failure: a container bound to localhost fails its health check with an error about the container failing to start listening, and the code looks correct. Check that in the Dockerfile, not here.