Why architecture matters here
Point-to-point integration is the architecture that quietly kills velocity. When service A must react to service B, the naive approach wires them together: B calls A directly, or A polls B's API, or someone stands up a bespoke Pub/Sub topic with custom schema and a handler that knows B's exact payload. Do this for ten producers and ten consumers and you have a hundred brittle edges, each duplicating auth, retry, and parsing, each a place where a schema change breaks a downstream nobody remembered existed. The system becomes a graph no one can hold in their head, and every new integration makes it worse. Event routing exists to replace that N-times-M mesh with an N-plus-M hub: producers emit to the router, consumers subscribe through the router, and the coupling collapses.
Eventarc's specific architectural value is that it makes the router universal on GCP without asking producers to change. The Cloud Audit Logs integration is the key: because virtually every Google Cloud API writes an audit log entry when it does something, Eventarc can trigger on those entries and thereby react to actions the source service never explicitly designed to emit events for. Create a VM, grant an IAM role, run a BigQuery job — each is an audit log event you can route. Combined with direct sources like Cloud Storage object changes and native Pub/Sub messages, this gives you one consistent mechanism to react to nearly anything happening in your project, which is a categorically different capability than wiring individual webhooks.
The normalization matters just as much. By delivering every event as a CloudEvents-formatted payload — a CNCF-standard envelope with consistent attributes for source, type, subject, and time — Eventarc lets a handler be written against one shape rather than dozens of proprietary formats. That standardization is what makes handlers portable and testable, and it's why choosing Eventarc is an architectural commitment to loose coupling and open formats rather than a pile of point integrations that ossify into technical debt.
The other architectural payoff is that Eventarc lets you inherit hard-won delivery guarantees instead of reimplementing them. Building your own event fabric means solving durability (don't drop events on a crash), retry with backoff (don't hammer a recovering consumer), dead-lettering (don't let one poison message block a queue forever), and authentication (don't let anyone invoke your handler) — and getting each of these subtly wrong is how homegrown event systems quietly lose data for months before anyone notices. By riding Pub/Sub, Eventarc gives you at-least-once durability, exponential-backoff retry, and dead-letter topics as table stakes, and by minting per-trigger OIDC identities it handles authentication without you shipping secrets. That means the architectural decision to adopt Eventarc is also a decision to stop owning a category of undifferentiated, easy-to-botch plumbing, which is usually the right call: your team's value is in the handlers, not in re-deriving delivery semantics that a managed router already gets right.
The architecture: every piece explained
Top row: sources and transport. Direct sources emit events Eventarc understands natively — Cloud Storage object finalize/delete/archive, Pub/Sub messages, Firebase events, and a growing set. The broadest source is Cloud Audit Logs: Eventarc subscribes to audit log entries and turns a matching entry (this service, this method name, optionally this resource) into an event, which is how it reaches 130-plus services that have no bespoke event API. Underneath, the Pub/Sub bus is the transport backbone — Eventarc provisions the topics and subscriptions, and for a Pub/Sub source you can even bring your own topic. This layering means Eventarc's delivery semantics are Pub/Sub's semantics: at-least-once, durable, retried.
Top-right: the routing decision. A trigger is the declarative rule that connects a source to a target. It carries filters on CloudEvents attributes — the event type (e.g. object finalized), the source service, and for audit-log triggers the service name, method name, and resource name — expressed as exact matches or path patterns. Only events matching every filter are delivered, so a bucket generating thousands of events routes just the finalize events for a given prefix to your handler. The trigger also names the target and the service account Eventarc uses to authenticate the push.
Middle row: normalization and delivery. Every routed event is wrapped as a CloudEvent — a normalized envelope with standard attributes and the source-specific payload as data. Delivery is a push: Eventarc invokes the target over authenticated HTTP (an OIDC token for Cloud Run/Functions) with at-least-once semantics. Targets are the serverless and container compute — Cloud Run services and jobs, GKE workloads, Cloud Functions, and Workflows for multi-step orchestration. Because delivery rides Pub/Sub, failed pushes are retried with backoff, and after configured attempts the event lands in a dead-letter topic instead of blocking the subscription forever.
Bottom row: the handler's responsibilities and the platform's limits. The handler must be idempotent because at-least-once means duplicates will happen; process the same event twice and it must produce the same result. Two limits are load-bearing: Eventarc provides no global ordering across events by default (a later object write can be delivered before an earlier one), and deduplication is the handler's job. The ops strip names what you watch — dead-letter queue depth (the primary health signal), acknowledgment latency, and a maintained inventory of triggers so the routing graph stays comprehensible.
End-to-end flow
Trace an image-processing pipeline. A user uploads a photo; the mobile app writes it to a Cloud Storage bucket. The object finalize completes, and Cloud Storage emits an event. Eventarc's trigger for this bucket — filtering on the finalized event type and a path prefix of uploads/ — matches. Eventarc wraps the notification as a CloudEvent (type google.cloud.storage.object.v1.finalized, subject the object path, plus bucket and generation attributes) and pushes it over authenticated HTTP to a Cloud Run service, minting an OIDC token from the trigger's service account so the service can verify the caller.
The Cloud Run handler receives the CloudEvent, extracts the bucket and object name, and generates thumbnails. Crucially it is idempotent: it derives the output object names deterministically from the input generation, so if the same event is delivered twice — which at-least-once makes inevitable — the second run overwrites identical thumbnails and returns 200 without double-charging any downstream. It acknowledges by returning a 2xx; Eventarc marks the event delivered and Pub/Sub drops it from the subscription.
Now the failure paths. Suppose the handler is mid-deploy and returns 503 for a window. Eventarc doesn't lose the event: Pub/Sub retries with exponential backoff, and once the new revision is healthy the push succeeds. Suppose instead a specific image is corrupt and the handler throws on it every time. After the configured maximum delivery attempts, that event is routed to the dead-letter topic rather than blocking the subscription or retrying forever — a separate consumer (or an alert on DLQ depth) surfaces it for human triage, and the pipeline keeps flowing for every other image. Ordering shows its edge here too: if the user rapidly uploads then overwrites the same object, the finalize events may arrive out of order, so the handler keys on the object generation (monotonic) and ignores an event whose generation is older than what it already processed — the application supplies the ordering the platform doesn't.
A second trigger on the same architecture shows the audit-log path: a compliance service must react whenever anyone makes a storage bucket public. There is no native 'bucket made public' event, but the IAM setPolicy call writes a Cloud Audit Log entry. An Eventarc trigger filtering on the storage service, the SetIamPolicy method, and the relevant resource fires on that entry, delivers a CloudEvent to a Workflows target, and the workflow inspects the new policy and reverts or alerts. One event router, two completely different sources — a direct storage event and a synthesized audit-log event — delivered to two targets through the same triggers-and-filters mechanism, with the same retry and dead-letter safety net.
Two operational details from this flow separate a robust deployment from a fragile one. First, the acknowledgment contract: the handler must return a 2xx within the ack deadline, or Pub/Sub assumes the delivery failed and redelivers — so a handler that does thirty seconds of thumbnail work inside a ten-second ack window doesn't just risk a timeout, it guarantees a retry storm where the same event is delivered again and again while the first attempt is still running. The fix is to ack fast and offload: accept the event, hand the heavy work to a Cloud Run job or a Workflow, and return 200 immediately, so the ack reflects 'received and durably queued' rather than 'fully processed'. Second, the dead-letter path is not optional scaffolding; it is the difference between one corrupt image being quarantined for a human to inspect and that same image wedging the subscription so every subsequent upload backs up behind it. Configuring a dead-letter topic and alerting on its depth turns an unbounded failure into a bounded, observable one — which is exactly the property you want when the pipeline is processing millions of events a day and you cannot watch each one. Taken together, these two habits — ack fast and offload, and always dead-letter — are what let a small team run an Eventarc-driven pipeline at scale without treating every deploy or every malformed payload as a potential outage.