AWS EventBridge in Depth
EventBridge is AWS’s managed event bus, and it is one of those services that looks simple until you look closely—at which point you realize it has quietly become the connective tissue for a wide range of AWS architectures. It receives events from AWS services, custom applications, and SaaS partners; routes them to targets through pattern-matched rules; and, through its Pipes and Scheduler extensions, handles point-to-point integrations and scheduled invocations that previously required purpose-built infrastructure.
This post covers EventBridge from its core routing model through the full surface area of its capabilities: event pattern matching, schema discovery, archive and replay, Pipes, Scheduler, cross-account routing, and the event-driven patterns it enables. The goal is not just to describe what exists but to establish when each feature earns its place in an architecture.
Event Buses and the Routing Model
Every AWS account has a default event bus. It receives events automatically from over 200 AWS services: EC2 state changes, CodePipeline stage transitions, CloudFormation stack updates, ECS task state changes, RDS events, and hundreds more. You cannot delete the default bus; you can add rules to it.
Custom event buses are what you create for your own applications. You can have up to 100 per region. The distinction matters: AWS service events go to the default bus, your application events should go to custom buses. Mixing them forces your rules to filter more aggressively and makes it harder to reason about the bus’s purpose.
Partner event buses receive events from SaaS providers—Zendesk, PagerDuty, Shopify, Datadog, and dozens of others—that have integrated with EventBridge’s partner event source model. A partner creates an event source in AWS Partner Network; you subscribe to it and create a custom bus associated with it.
Event Structure
Every EventBridge event is a JSON object with eight reserved top-level fields:
|
|
The source and detail-type fields are the primary routing dimensions. source identifies the producing application (use reverse-DNS notation to avoid collisions: com.myapp.orders). detail-type identifies the specific event type within that source. Together they define the event contract. Everything in detail is your event payload; EventBridge imposes no schema on it.
Payload billing is in 64 KB chunks: a 100 KB payload counts as 2 events for billing purposes. For custom events, this is $1.00 per million (rounded up to 64 KB boundaries), so a 200 KB payload costs $2.00 per million invocations. Keep event payloads small—put large data in S3 and include the reference in the event.
Publishing Events
From application code:
|
|
put_events accepts up to 10 events per call and up to 256 KB total per call. The call is synchronous—it returns a success or failure per event, not a delivery guarantee. EventBridge is at-least-once delivery; your consumers must be idempotent.
Rules and Event Pattern Matching
A rule on an event bus consists of a pattern (which events to match) and one or more targets (where to send matching events). Each bus can have up to 300 rules; each rule can have up to 5 targets. Events that match a rule are sent to all matching targets simultaneously and independently.
Pattern Syntax
Event patterns use JSON with a specific matching semantics. An empty pattern {} matches all events. To match specific fields, specify the expected values as arrays:
|
|
This matches events where source is com.myapp.orders, detail-type is Order Placed, and the shipping country is US, Canada, or UK. Multiple field constraints are ANDed; multiple values within a field are ORed.
Beyond exact matching, EventBridge supports several comparison operators:
Prefix and suffix matching:
|
|
Anything-but (exclude specific values):
|
|
Numeric range matching:
|
|
Exists / does-not-exist (check field presence):
|
|
$or for field-level OR logic across different fields:
|
|
IP address CIDR matching (useful for webhook-sourced events that include IP):
|
|
Pattern matching happens server-side—EventBridge evaluates patterns before invoking targets. No compute costs for events that do not match any rule.
Input Transformation
Before sending an event to a target, you can transform the payload. Transformations use JSONPath to extract fields and a template to construct the target payload:
|
|
Use input transformations to reduce payload size before reaching Lambda (staying within Lambda’s 6 MB payload limit), to restructure the event to match a target API’s expected schema, or to extract a subset of fields for a downstream service that doesn’t need the full event.
Schema Registry and Discovery
The Schema Registry is a catalog of event schemas. When you enable schema discovery on an event bus, EventBridge samples incoming events, infers their JSON Schema, and registers schemas automatically. As event structure evolves, new schema versions are created.
Discovery is free for the first 5 million events per month; $0.10 per million beyond that. It is worth enabling on all custom buses in development and staging environments.
From the AWS console or CLI, you can generate typed code bindings from a registered schema:
|
|
The generated code includes typed classes for the event’s detail object with IDE autocomplete and type checking. In a large service mesh where multiple teams produce and consume events, schema bindings prevent runtime deserialization errors from schema drift.
Schema versioning also serves as documentation. A new engineer exploring the codebase can look at the schema registry to understand what events exist, what fields they contain, and how they have evolved over time—without reading producer code.
Archives and Replay
Archive an event bus to retain all events (or a filtered subset) for a configurable retention period. Archived events can be replayed against current rules at any time.
|
|
The FilterArns parameter restricts replay to specific rules—you can replay events through only the new rule you added, without re-invoking existing rules for events that already processed successfully.
Replay is charged at $1.00 per million events replayed (in addition to normal storage at $0.10/GB/month).
When Archive and Replay Earns Its Cost
Backfilling a new consumer service: You add a new microservice that processes Order Placed events to build a separate analytics projection. You need to populate its state from historical events. Without archive, you would need to backfill from the database or rebuild event history. With archive, replay the last 30 days into the new rule.
Recovery from a consumer outage: Your inventory service had a 2-hour outage. During that time, 50,000 Order Placed events fired and the inventory service’s Lambda failed to process them. The events are in the archive; replay them after the fix is deployed.
Testing rule changes: You modify a routing rule and want to verify the new behavior against real production events before making it live. Create a test rule pointing to a test target, replay a day of archived events through it, and inspect the test target’s logs.
EventBridge Pipes
Pipes are a point-to-point integration primitive. Where rules route events from a bus to targets, Pipes create a direct pipeline from a source to a target—with optional filtering, enrichment, and transformation in between. Pipes replace the Lambda-as-glue pattern for common source-to-target connections.
Source → Filter → Enrichment → Transform → Target
Sources
- Amazon SQS (standard and FIFO)
- DynamoDB Streams
- Kinesis Data Streams
- Amazon MSK (Kafka)
- Self-managed Kafka
- Amazon MQ (ActiveMQ and RabbitMQ)
Targets
- Lambda
- Step Functions
- SQS, SNS
- EventBridge event buses
- API Gateway
- Kinesis Data Streams
- DynamoDB
- CloudWatch Logs
- Any of the 6,000+ AWS API actions via EventBridge API destinations
A DynamoDB Streams to EventBridge Example
A common pattern: changes to a DynamoDB table should generate business events on the EventBridge bus. Without Pipes, this requires a Lambda function to translate DynamoDB Streams records into business events and publish them. With Pipes:
|
|
Without Pipes, this would be a Lambda function with DynamoDB Streams trigger, error handling, retry logic, and CloudWatch alarms. The Pipe manages the event source mapping, handles partial batch failures, and provides built-in CloudWatch metrics.
Ordering Guarantees
Pipes respect the ordering characteristics of their source:
- SQS FIFO: strict ordering within message group
- Kinesis: strict ordering within shard
- DynamoDB Streams: strict ordering within partition key
- Standard SQS: no ordering guarantee
If ordering matters, choose a source that provides it. If throughput matters more than ordering, use standard SQS.
Pricing
$0.40 per million events processed after filtering. If your filter drops 90% of events, you pay only for the 10% that pass through. Design filters to be as selective as possible.
EventBridge Scheduler
Scheduler replaces EventBridge Rules-based scheduling (the rate() and cron() expressions on rules) with a first-class scheduling primitive that is dramatically more capable.
Key advantages over cron-on-rules:
- 14 million free invocations per month (permanent, global—not per-region)
- Supports individual per-schedule timezones with DST handling
- Scales to millions of schedules (rules have a 300-per-bus limit)
- Universal targets: any of the 6,000+ AWS API actions
- Flexible time windows for load distribution
- Dead-letter queues and retry configuration per schedule
Schedule Types
Rate-based: Fire on a fixed interval.
|
|
Cron-based: Fire at a specific time. The cron format is minutes hours day-of-month month day-of-week year (6 fields, unlike the 5-field Unix cron):
|
|
One-time: Fire once at a specified time.
|
|
ActionAfterCompletion: DELETE automatically removes the schedule after it fires—no cleanup required.
Flexible Time Windows
The FlexibleTimeWindow setting allows Scheduler to invoke the target within a window around the scheduled time rather than exactly at it. This distributes invocations to avoid thundering herd when thousands of schedules fire at the same minute:
|
|
A rate-based schedule with a 15-minute flexible window fires between T+0 and T+15 of each hour. For workloads where exact timing is unimportant, this reduces downstream load spikes significantly.
Universal Targets
Scheduler can invoke any AWS SDK API action directly without Lambda as a relay. A schedule that disables unused IAM users after 90 days of inactivity:
|
|
More practically, universal targets shine for single-purpose invocations: stopping an RDS instance outside business hours, snapshotting an EBS volume, invoking a Step Functions state machine, sending a message to SQS.
Replacing Cron Jobs
A cron job running on an EC2 instance or in a Kubernetes CronJob has several failure modes: the instance or pod may be down at the scheduled time; there is no built-in retry or dead-letter handling; timezone drift and DST handling require care; monitoring is whatever the operator wires up manually.
Scheduler eliminates all of these. The tradeoff: it is limited to AWS API invocations (via Lambda, Step Functions, or direct SDK calls). Shell scripts and arbitrary process execution require a Lambda wrapper. For most scheduled cloud infrastructure work, that is not a meaningful constraint.
Cross-Account and Cross-Region Routing
Cross-Account
To send events from account A to account B, attach a resource policy to account B’s target bus granting account A permission to put events:
|
|
Then in account A, create a rule targeting account B’s bus:
|
|
The IAM role for the rule needs events:PutEvents permission on the destination bus ARN.
This pattern enables a hub-and-spoke security architecture: spoke accounts emit audit, compliance, and security events; a central security account receives and processes them. No spoke account can modify the central processing; cross-account delivery is unidirectional.
Global Endpoints for Multi-Region HA
Global Endpoints are Route 53-backed DNS endpoints that provide automatic failover between two regional event buses:
Applications in any region
|
▼
Global Endpoint (Route 53 DNS)
├── Primary: us-east-1 event bus (healthy)
└── Secondary: eu-west-1 event bus (standby)
When the primary region’s health check fails, Route 53 DNS begins routing PutEvents calls to the secondary region’s bus. Event replication automatically copies all custom events to both buses continuously, so the secondary bus has the same rules and historical events as the primary.
Global endpoints do not add cost beyond normal event ingestion pricing. Enable them for any event bus where producer availability during a regional outage matters.
Error Handling and Dead Letter Queues
EventBridge rule targets have configurable retry policies. The default is exponential backoff for up to 24 hours and 185 retry attempts. For most targets this is appropriate; reduce the window for time-sensitive events where a 24-hour-old retry is worse than no retry:
|
|
The DLQ must be an SQS standard queue (not FIFO). Events that exhaust retries or are immediately non-retryable land in the DLQ with metadata:
|
|
Set a CloudWatch alarm on InvocationsSentToDLQ > 0 for any production rule. A non-zero DLQ count is always worth investigating—it means events are being dropped.
Immediate failure cases—missing permissions, deleted target resource, invalid ARN—go directly to the DLQ without retries. These will keep failing until the underlying issue is fixed, so catching them quickly matters. The Lambda execution role for the rule target and the Lambda function itself must both exist and have proper permissions.
Real-World Patterns
Decoupling Microservices
The core EventBridge use case: services emit events without knowing who consumes them. The order service emits Order Placed; the inventory service, notification service, and analytics service each independently subscribe with their own rules. Adding a new consumer requires no changes to the producer—just add a new rule.
Order Service
│ emit: Order Placed
▼
EventBridge Bus (orders-bus)
├── Rule: Process inventory → Inventory Lambda
├── Rule: Send confirmation → Notification Lambda
├── Rule: Update analytics → Analytics Lambda
└── Rule: Sync to ERP → SQS queue → ERP Consumer
Each consumer processes independently. If the analytics Lambda is slow, it does not block the inventory update. If the notification service has a brief outage, EventBridge retries for up to 24 hours.
The downside: fan-out makes end-to-end tracing harder. Include a correlation ID in every event’s detail and propagate it through all consumers. If you use OpenTelemetry, inject the trace context as well:
|
|
The Saga Pattern for Distributed Transactions
A saga is a sequence of local transactions where each step emits an event that triggers the next step, and each step has a compensating transaction to undo it if a later step fails. EventBridge enables the choreography variant; Step Functions enables the orchestration variant.
Choreography (EventBridge-native):
Customer clicks "Book Trip"
│
▼ emit: TripBookingRequested
EventBridge
│
├── Flight Service: Book flight → emit: FlightBooked or FlightFailed
│
├── Hotel Service (on FlightBooked): Book hotel → emit: HotelBooked or HotelFailed
│
├── Car Service (on HotelBooked): Book car → emit: CarBooked or CarFailed
│
└── On any *Failed event: Compensation chain fires
├── Cancel car (if booked)
├── Cancel hotel (if booked)
└── Cancel flight (if booked)
Each service owns its step and its compensating transaction. There is no central coordinator—the event flow is the coordination mechanism. This is simple to reason about per service but hard to trace as a whole. Use correlation IDs and a dedicated saga state table in DynamoDB to track in-flight sagas.
Orchestration (Step Functions + EventBridge):
Step Functions coordinates the saga with a central state machine. EventBridge triggers the state machine (StartExecution via Scheduler or event rule), and the state machine calls each service in sequence, handles failures, and executes the compensation steps in reverse order via parallel branches on failure. This is harder to implement but much easier to observe—the Step Functions execution graph shows exactly where a saga failed and why.
Choose choreography when the saga has a small number of steps (< 5) and each step is simple. Choose orchestration when the saga has complex branching, requires precise compensation ordering, or needs human-readable audit trails.
Replacing Cron Jobs with Scheduler
A recurring pattern for operations teams: maintenance windows, snapshot schedules, daily report generation, stale data cleanup. Each historically required a cron entry on an EC2 instance or a Kubernetes CronJob.
With Scheduler, each job becomes a schedule with a Lambda or Step Functions target. The entire maintenance schedule can be managed as Terraform:
|
|
The free tier of 14 million invocations per month covers essentially all maintenance workloads. The real value is not cost but observability: CloudWatch metrics for each schedule, DLQ for failed invocations, and no dependency on a running server.
What EventBridge Is Not Good At
EventBridge is not a streaming platform. It has no concept of consumer groups, partition-level ordering, or high-throughput replay. For workloads requiring ordered, high-throughput event streaming with long-term retention and multi-consumer fan-out at millisecond latency, Kinesis or Kafka are appropriate. EventBridge targets throughput in the hundreds of thousands of events per second for fan-out distribution, not the millions-per-second of dedicated streaming platforms.
EventBridge is also not a workflow engine. Rules with multiple targets run targets in parallel with no coordination. If you need step sequencing, conditional branching, error handling with retry, or human approval steps, combine EventBridge with Step Functions—EventBridge routes the triggering event; Step Functions orchestrates the workflow.
The combination is powerful: EventBridge for the routing and fan-out layer, Step Functions for stateful orchestration, Scheduler for time-triggered invocations. These three services together handle the vast majority of event-driven coordination requirements in a cloud-native application without additional infrastructure.
Comments