Deliver Pub/Sub messages to a Cloud Run service, with the failure paths closed

Six steps for a delivery path that survives contact with production: decide push or pull first, make the consumer idempotent because the default guarantee requires it, pick the Cloud Run resource type, configure the dead-letter topic before go-live, authorise the delivery, then verify by failing on purpose.

markdownguidev1QYGE-0A5Ystage: buildtrust: officialfamily: messagingservice: pubsubinterface: anypub/sub to cloud runpush subscriptionpull subscriptionidempotent consumerdead-letter topicworker poolat-least-onceroles/run.invokerordering key

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

Pub/Sub to Cloud Run

Step 1 — push or pull, decided deliberately

PushPull
who sets the pacePub/Subthe consumer
what you runa Cloud Run service with an endpointa consumer that fetches
failure modeyour service's 429/503 becomes a redeliveryyour consumer's backlog

Push when the work is small and bursty and you want nothing to run idle. Pull when the downstream is fragile or rate-limited and the consumer must control the flow. Changing your mind later is a subscription change, which is cheap — but it changes which Cloud Run resource type you need, which is not.

Step 2 — pick the Cloud Run resource type to match

  • push → a Cloud Run service: it is request-driven and scales on requests, which is exactly what a

push subscription produces;

  • pull → a worker pool: no public endpoint, and it does not scale on requests, which is correct

because there are none.

A pull consumer deployed as a service will not scale, because no requests arrive (reference/compute/cloud-run.md §1).

Step 3 — make the consumer idempotent. This is not optional

Standard subscriptions are at-least-once. The consumer will see a message twice. Give every message a natural key and make processing safe to repeat.

Only after that is true, consider enabling exactly-once delivery — as a reduction in noise, never as the thing that makes correctness possible.

Step 4 — configure the dead-letter topic now, not after the first incident

A subscription without one retries a permanently-failing message until retention expires, and then the message is gone with no record. Create the dead-letter topic, point the subscription at it, and give it a subscription of its own so somebody sees what lands there.

Step 5 — authorise the delivery

For a push subscription the Pub/Sub push identity must be allowed to invoke the service — roles/run.invoker on it. Keep the service otherwise private: a push endpoint open to the internet is an unauthenticated write path into your processing.

For a pull consumer, the worker pool's own service account needs subscriber permission on the subscription.

Step 6 — verify by failing on purpose

TestExpected
publish one messageprocessed once
publish the same message twiceone net effect. If there are two, step 3 is not done
make the consumer fail permanently for one messagethe message lands in the dead-letter topic
load it past the instance ceiling (push)429 — capacity. Raise maximum instances, or move to pull
break the secret the consumer reads503 — configuration, not capacity
if ordering is on, publish many messages with one ordering keythroughput serialises. That is the trade; use a high-cardinality key

The second and third rows are the ones people skip, and they are the two that decide whether this path is production-grade.

For a migration

Before building any of this, check whether you need a consumer at all: a BigQuery, Bigtable or Cloud Storage subscription writes messages straight into those services with no code (reference/messaging/pubsub.md §2). A migrated architecture frequently carries a consumer whose entire job Pub/Sub will now do. See guides/parity/messaging.md.

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