Cloud Run request timeouts don't kill your code (so your architecture has to)
A Cloud Run request timeout ends the request, not necessarily the work. If the operation can outlive its caller, the system needs explicit job semantics instead of hope.
A Cloud Run request timeout closes the connection and returns a 504. It doesn’t terminate the container instance, and the code may continue processing after the caller has been told the request failed. The timeout is therefore a transport deadline, not an execution boundary.
If correctness depends on work stopping when the response window ends, the architecture is relying on a guarantee Cloud Run doesn’t provide.
The failure is ambiguous by default
Consider a handler that writes a record, calls an external API, and then updates status. The request times out after the external call but before the final update.
The caller sees failure and retries. The first invocation may still be running. The external system may have accepted the operation. The application now has two executions and no transport-level evidence that says which side effect happened.
A longer timeout reduces how often this exact sequence occurs. It doesn’t remove it. Clients disconnect, proxies have their own deadlines, networks fail, and long Cloud Run requests are more likely to need reconnection and retry handling.
A 504 must therefore mean only one thing: the caller didn’t receive a response in time. It says nothing reliable about domain completion.
Keep bounded request work inside the request
Synchronous execution is appropriate when the operation is short, its side effects can be committed atomically or idempotently, and the caller genuinely needs the result before proceeding.
Use an application deadline shorter than the Cloud Run deadline. Pass cancellation or remaining-time signals through database and HTTP clients. Stop starting new work when there isn’t enough time to finish it safely. Roll back transactions and return a classified error while the platform can still deliver it.
This doesn’t make cancellation perfect. It makes the handler cooperate with a known deadline instead of discovering it when the connection disappears.
The request path should have an explicit answer for:
- the maximum expected execution time
- which calls receive shorter child deadlines
- what can be retried safely
- which side effects use idempotency keys
- what state remains when the caller disconnects
If those answers are vague, increasing timeoutSeconds only extends the period of ambiguity.
Move durable work behind acceptance
When work can outlive the caller, persist the intent and give execution another owner.
The request validates input, creates a job or command with a stable idempotency key, and returns an identifier. A queue, Cloud Tasks target, workflow, Cloud Run job, or another worker processes the operation. The client reads status or receives a callback later.
The durable record needs real states such as pending, running, succeeded, failed, cancelled, and retryable. Store the external operation identifiers needed to reconcile ambiguous responses. A worker retry should continue or deduplicate the same command, not create another interpretation of the user’s intent.
This split also makes timeouts easier to classify. The acceptance request either persisted the command or it didn’t. The worker has its own execution deadline and retry policy. Neither layer infers completion from whether an HTTP client remained connected.
Idempotency covers both client and platform retries
Generate the idempotency key at the boundary where business intent becomes durable. Reusing a request ID generated inside each attempt is useless. Every retry will look new.
Enforce uniqueness where the side effect begins. For a payment, message, export, or external write, store the key and result before returning success. When the downstream API supports idempotency, pass the same key through. When it doesn’t, use a local state machine and reconciliation rather than pretending exactly-once delivery appeared through optimism.
Timeouts are only one source of duplicate attempts. Deployments, worker crashes, task retries, and operators produce the same requirement.
Observe execution separately from response delivery
Log the request ID, command or job ID, idempotency key, deadline, side effects attempted, external identifiers, and terminal state. Metrics should distinguish:
- requests that reached their deadline
- commands accepted but still running
- worker retries
- ambiguous downstream outcomes
- completed work whose original response was lost
Without that separation, a timeout graph becomes a poor proxy for business state.
Choose who owns the work
Keep work in a Cloud Run request when it is naturally request-shaped and can remain correct if the caller retries. Use a durable execution model when the operation needs to survive disconnection, run longer than the response contract, or coordinate side effects that require reconciliation.
Cloud Run defines what its request timeout means. The architecture has to be equally precise about who owns the work after the request ends.
More in this domain: Infrastructure
Browse allHow we decide between Cloud SQL connectors, Auth Proxy, and private IP
Cloud SQL connectors, the Auth Proxy, and private IP are not interchangeable secure connection options. They change identity, routing, deployment shape, and how much network plumbing the team actually owns.
Safe scaling defaults for Cloud Run + Postgres
Cloud Run autoscaling is not a database strategy. Safe defaults keep the application from scaling itself into a Postgres incident before the team understands the workload.
IAM DB auth for Cloud SQL: when it simplifies security and when it complicates delivery
IAM DB auth can reduce password sprawl and make revocation cleaner, but it also turns database access into an identity operating model that depends on disciplined service-account boundaries.
Cloud Run scaling from zero is a feature until it isn't
Scale to zero is a good default for request-driven services, until startup delay, warm-capacity needs, or instance caps turn it into user-visible reliability behavior instead of a pricing feature.
Direct VPC egress vs Serverless VPC Access for Cloud Run: our default
We default to Direct VPC egress for Cloud Run because it is the cleaner networking shape: fewer moving parts, no connector resource, and costs that scale with the service instead of beside it.
Related patterns
"Internal-only" Cloud Run isn't just a checkbox
Making a Cloud Run service private is not one toggle. It is a decision about ingress, routing, caller path, and IAM working together as one access model.
GKE Autopilot as the escape hatch from Cloud Run
When Cloud Run stops fitting, the next move is usually GKE Autopilot: more Kubernetes-shaped control without immediately taking on the full burden of Standard clusters.
Why we default to Cloud Run for SME internal platforms
For SME internal platforms, Cloud Run is our default because it covers a large share of useful workload shapes without forcing teams to own cluster operations before they have earned that surface area.
How we treat Terraform state in team environments
Terraform starts feeling fragile in teams when state is treated like a backend setting instead of a shared dependency for safe change.