Cloud Tasks vs. Pub/Sub for Cloud Run: routing, retries, and rate limits
Use Cloud Tasks for a specific operation that needs scheduling or controlled dispatch. Use Pub/Sub when independent consumers need the same event. Either HTTP handler must tolerate retries.
For a worker that sends a reminder, generates an invoice, or calls a rate-limited API, we’d usually choose Cloud Tasks. The application knows which operation it needs and which handler should perform it. The queue gives that operation a dispatch rate, a schedule when needed, and a retry policy.
Pub/Sub is a better starting point when an event needs independent consumers. An order.accepted event can reach billing, fulfillment, and analytics without the publisher managing their separate progress. Each consumer gets its own subscription. Several worker instances sharing one subscription distribute its deliveries; they don’t each receive an independent copy.
These are defaults, not a restriction that events must use Pub/Sub and commands must use Tasks. Choose the delivery controls you need. Both can invoke a Cloud Run service over HTTP, as Google’s comparison sets out.
The differences that affect the worker
| Requirement | Cloud Tasks | Pub/Sub push |
|---|---|---|
| Routing | An explicit HTTP target, with queue-level routing available. | A topic and independently configured subscriptions. |
| Pacing and scheduling | Queue dispatch rate, concurrent dispatch limit, and task scheduling. | Delivery adapts to subscriber responses; no per-message schedule. |
| Maximum HTTP attempt duration | 30 minutes. | 10 minutes. |
| Independent consumers | Create separate tasks when separate operations are needed. | Create separate subscriptions. |
Those duration limits apply to an individual delivery attempt, not the lifetime of the business operation. A longer Cloud Run timeout doesn’t extend an upstream deadline. Queue delivery also isn’t an exact appointment: scheduling makes a task eligible for dispatch, but load and retries can delay completion.
Put rate limits around the dependency that needs them
Suppose a partner API permits only a small number of concurrent calls. Increasing the Cloud Run instance count won’t increase that allowance. We’d put calls to that partner behind a dedicated Cloud Tasks queue, then configure both dispatch rate and maximum concurrent dispatches.
The Cloud Tasks queue controls documented by Google use a token bucket, so a configured rate can still permit a burst. A strict external quota may require an additional limiter at the outbound call, especially when other queues or applications share the allowance. Count retries and multiple API calls within one task too. Ten tasks per second isn’t ten partner requests per second when each task makes three requests.
Separate queues when work has different limits or urgency. A bulk resynchronization shouldn’t use all the dispatch capacity reserved for customer-facing work. There is less reason to create a queue for every customer when all of them share the same provider limit and operating policy.
Pub/Sub push delivery isn’t a partner-call rate limiter. A subscriber can enqueue a Cloud Task when those calls need explicit pacing or scheduling.
Independent subscriptions don’t establish business order
Billing can be current while fulfillment is behind. That independence is valuable when the workflows can progress separately. It is wrong when fulfillment must wait for billing to finish.
In that case, publish a later event after billing commits or express the dependency in a workflow. Delivering the original event to both systems doesn’t establish the required sequence. Track backlog age and failures for each subscription so a successful publish isn’t mistaken for completed work.
Cloud Tasks isn’t a FIFO workflow engine; Google documents that execution order isn’t guaranteed. A task that depends on another operation needs an explicit prerequisite or a later enqueue, not an assumption about which task was created first.
Return success after completion or a durable handoff
Cloud Tasks treats a 2xx response as success. Pub/Sub push accepts its documented acknowledgment codes, including 200, 202, and 204. The relevant contracts are in Google’s Cloud Tasks HTTP handler documentation and Pub/Sub push documentation.
Returning 202 and leaving a background promise running gives up the queue’s retry protection. Return success after the operation completes, or after another durable system has accepted responsibility. For a Cloud Run job, that means tracking its execution and handling its eventual failure; accepting the start request doesn’t prove that the job finished.
An unsuccessful response doesn’t prove that nothing happened either. The handler might have committed its side effect before the response was lost. A delivery deadline can also expire while Cloud Run continues processing, allowing the next attempt to overlap the first.
Use a stable business operation key, such as (invoice_id, document_version). For database-only work, record completion in the same transaction as the effect. For an external API, reuse the provider’s idempotency key when supported. Without that support, a timeout after the external call may need reconciliation rather than a blind retry. A local done flag cannot close the gap between an external success and a crash before recording it.
Cloud Tasks creation deduplication doesn’t make execution exactly once. The Pub/Sub exactly-once delivery option documented by Google applies to pull subscriptions, not the push endpoint in this comparison.
Keep a record beyond the delivery attempt
There is a failure window before the queue receives anything: the application commits an order, then crashes before enqueueing or publishing. When losing the follow-up is unacceptable, write an outbox record in the same database transaction and let a relay deliver it. The relay may publish more than once after an ambiguous result, so the consumer still needs the operation key.
Keep durable business status for work that must be accounted for. A missing task isn’t proof of success: retry or retention limits can also remove work. A configured Google Cloud Pub/Sub dead-letter topic needs a consumer or repair process, not just a resource in the deployment.
Before increasing volume, test a timeout after the side effect, a duplicate while the first attempt is still running, a permanently invalid payload, and exhausted retries. Also test the actual private invocation path with the queue or subscription’s identity. The operator should be able to find unfinished work and determine what a retry will do without inferring success from an HTTP log.
More in this domain: Infrastructure
Browse allCloud Run scale to zero: cold starts and minimum instances
Keep scale to zero when the first request can wait. Pay for minimum instances when measured startup latency hurts the service, and decide background CPU separately.
Direct VPC egress vs. Serverless VPC Access for Cloud Run
Use Direct VPC egress for most new Cloud Run connections to a VPC. Check address capacity, startup behavior, and outbound routing before retiring a working connector.
Internal-only Cloud Run: ingress, identity, and the caller’s network path
An internal Cloud Run service needs an allowed request path and an authorized caller. The correct setup differs for Cloud Tasks, another Cloud Run service, and human access.
Pulumi stack config: values, code, and secrets
Stack config supplies values within an infrastructure design. Keep topology and operating policy explicit in code, validate the inputs, and manage secrets according to who owns their lifecycle.
How 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.
Related patterns
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.
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.
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.