AWS SQS vs SNS vs EventBridge: Messaging Compared
Compare AWS SQS, SNS, and EventBridge for messaging and event-driven architectures. Learn when to use each service with practical examples.
What you'll learn
- ✓Understand the core differences between SQS, SNS, and EventBridge
- ✓Choose the right messaging service for your architecture
- ✓Build event-driven workflows with filtering and routing
- ✓Combine services for complex messaging patterns
Prerequisites
- •AWS account with basic permissions
- •Familiarity with distributed systems concepts
AWS offers three main messaging services: SQS for queuing, SNS for pub/sub notifications, and EventBridge for event routing. They solve different problems, and production architectures often use all three together. Choosing the wrong service leads to unnecessary complexity, higher costs, or architectural dead ends.
This guide compares all three services side by side, shows when to use each one, and demonstrates how to combine them effectively.
Quick Comparison
Before diving deep, here is the high-level picture:
SQS (Simple Queue Service):
Pattern: Point-to-point queue
Model: Pull (consumers poll the queue)
Delivery: One consumer processes each message
Use case: Decoupling, work distribution, buffering
SNS (Simple Notification Service):
Pattern: Pub/sub fan-out
Model: Push (SNS delivers to subscribers)
Delivery: All subscribers receive every message
Use case: Broadcasting events to multiple consumers
EventBridge:
Pattern: Event bus with content-based routing
Model: Push (EventBridge routes to targets)
Delivery: Matched targets receive filtered events
Use case: Event-driven architectures, cross-service integration
SQS: Reliable Message Queuing
SQS is a fully managed message queue that decouples producers from consumers. A producer sends a message to a queue, and a consumer polls the queue to receive and process it. Each message is processed by exactly one consumer.
Standard vs FIFO Queues
# Standard Queue
Ordering: Best effort (not guaranteed)
Throughput: Nearly unlimited
Delivery: At-least-once (rare duplicates possible)
Use when: Order does not matter, high throughput needed
# FIFO Queue
Ordering: Strict first-in-first-out
Throughput: 300 msg/s (3,000 with batching)
Delivery: Exactly-once processing
Use when: Order matters (financial transactions, sequential processing)
Creating and Using SQS
# Create a standard queue
aws sqs create-queue \
--queue-name order-processing \
--attributes '{
"VisibilityTimeout": "60",
"MessageRetentionPeriod": "1209600",
"ReceiveMessageWaitTimeSeconds": "20"
}'
# Create a FIFO queue
aws sqs create-queue \
--queue-name order-processing.fifo \
--attributes '{
"FifoQueue": "true",
"ContentBasedDeduplication": "true",
"VisibilityTimeout": "60"
}'
Processing messages with a Lambda consumer:
import json
import boto3
sqs = boto3.client('sqs')
def handler(event, context):
for record in event['Records']:
body = json.loads(record['body'])
order_id = body['orderId']
try:
process_order(order_id, body)
print(f"Processed order {order_id}")
except Exception as e:
print(f"Failed to process order {order_id}: {e}")
# Raising an exception causes Lambda to NOT delete
# the message, so it returns to the queue
raise
def process_order(order_id, data):
# Business logic here
pass
# Lambda event source mapping for SQS
Resources:
OrderProcessor:
Type: AWS::Lambda::EventSourceMapping
Properties:
EventSourceArn: !GetAtt OrderQueue.Arn
FunctionName: !Ref ProcessOrderFunction
BatchSize: 10
MaximumBatchingWindowInSeconds: 5
FunctionResponseTypes:
- ReportBatchItemFailures
The ReportBatchItemFailures option lets your Lambda report which specific messages in a batch failed, so only those return to the queue.
Dead Letter Queues
Messages that fail processing repeatedly should go to a dead letter queue (DLQ) for investigation:
OrderQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: order-processing
VisibilityTimeout: 60
RedrivePolicy:
deadLetterTargetArn: !GetAtt OrderDLQ.Arn
maxReceiveCount: 3
OrderDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: order-processing-dlq
MessageRetentionPeriod: 1209600 # 14 days
After three failed attempts, messages move to the DLQ. Monitor the DLQ with CloudWatch alarms so you know when failures occur.
SNS: Pub/Sub Fan-Out
SNS delivers messages to multiple subscribers simultaneously. When a publisher sends a message to an SNS topic, every subscriber receives a copy. Subscribers can be SQS queues, Lambda functions, HTTP endpoints, email addresses, or SMS numbers.
Creating Topics and Subscriptions
# Create an SNS topic
aws sns create-topic --name order-events
# Subscribe an SQS queue
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:123456789012:order-events \
--protocol sqs \
--notification-endpoint arn:aws:sqs:us-east-1:123456789012:notification-queue
# Subscribe a Lambda function
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:123456789012:order-events \
--protocol lambda \
--notification-endpoint arn:aws:lambda:us-east-1:123456789012:function:process-analytics
# Subscribe an email address
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:123456789012:order-events \
--protocol email \
--notification-endpoint alerts@example.com
Message Filtering
SNS supports message filtering so subscribers only receive messages they care about. Without filtering, every subscriber gets every message and must discard irrelevant ones:
# Subscribe with a filter policy
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:123456789012:order-events \
--protocol sqs \
--notification-endpoint arn:aws:sqs:us-east-1:123456789012:high-value-orders \
--attributes '{
"FilterPolicy": "{\"orderValue\": [{\"numeric\": [\">\", 1000]}], \"status\": [\"confirmed\"]}",
"FilterPolicyScope": "MessageBody"
}'
This subscriber only receives messages where orderValue exceeds 1000 and status is “confirmed”. Filtering happens at the SNS level, so the subscriber queue never sees irrelevant messages.
Fan-Out Pattern: SNS + SQS
The most common SNS pattern is fan-out to multiple SQS queues. Each queue processes the same event differently:
Resources:
OrderEventsTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: order-events
# Queue 1: Send confirmation email
EmailQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: send-confirmation-email
EmailSubscription:
Type: AWS::SNS::Subscription
Properties:
TopicArn: !Ref OrderEventsTopic
Protocol: sqs
Endpoint: !GetAtt EmailQueue.Arn
# Queue 2: Update analytics
AnalyticsQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: update-analytics
AnalyticsSubscription:
Type: AWS::SNS::Subscription
Properties:
TopicArn: !Ref OrderEventsTopic
Protocol: sqs
Endpoint: !GetAtt AnalyticsQueue.Arn
# Queue 3: Update inventory
InventoryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: update-inventory
InventorySubscription:
Type: AWS::SNS::Subscription
Properties:
TopicArn: !Ref OrderEventsTopic
Protocol: sqs
Endpoint: !GetAtt InventoryQueue.Arn
Publishing one message to the topic delivers it to all three queues. Each queue has its own consumer processing at its own pace.
EventBridge: Content-Based Event Routing
EventBridge is the most powerful of the three services. It acts as a central event bus that routes events to targets based on rules that match event content. Unlike SNS, EventBridge understands event structure and can filter on any field in the event payload.
Key Advantages Over SNS
- Rich content filtering: Filter on any JSON field, including nested fields, numeric ranges, prefix matching, and logical operators
- Schema registry: Discover and validate event schemas automatically
- AWS service integration: Over 200 AWS services publish events to EventBridge natively
- Cross-account and cross-region: Route events between AWS accounts and regions
- Archive and replay: Store events and replay them for debugging or reprocessing
Creating Rules and Targets
# Create a custom event bus
aws events create-event-bus --name orders
# Create a rule that matches specific events
aws events put-rule \
--name high-value-orders \
--event-bus-name orders \
--event-pattern '{
"source": ["com.myapp.orders"],
"detail-type": ["OrderCreated"],
"detail": {
"total": [{"numeric": [">", 500]}],
"region": ["us-east-1", "us-west-2"],
"items": {
"category": [{"prefix": "electronics"}]
}
}
}'
# Add a Lambda target
aws events put-targets \
--rule high-value-orders \
--event-bus-name orders \
--targets '[{
"Id": "process-high-value",
"Arn": "arn:aws:lambda:us-east-1:123456789012:function:process-high-value-order",
"InputTransformer": {
"InputPathsMap": {
"orderId": "$.detail.orderId",
"total": "$.detail.total",
"customer": "$.detail.customerId"
},
"InputTemplate": "{\"orderId\": <orderId>, \"total\": <total>, \"customerId\": <customer>, \"priority\": \"high\"}"
}
}]'
The InputTransformer reshapes the event before delivering it to the target, so your Lambda function receives exactly the data it needs.
Publishing Events
import boto3
import json
from datetime import datetime
events = boto3.client('events')
def publish_order_event(order):
response = events.put_events(
Entries=[
{
'Source': 'com.myapp.orders',
'DetailType': 'OrderCreated',
'Detail': json.dumps({
'orderId': order['id'],
'customerId': order['customer_id'],
'total': order['total'],
'items': order['items'],
'region': 'us-east-1',
'timestamp': datetime.utcnow().isoformat()
}),
'EventBusName': 'orders'
}
]
)
if response['FailedEntryCount'] > 0:
print(f"Failed to publish: {response['Entries']}")
Event Archive and Replay
EventBridge can archive events for reprocessing:
# Create an archive
aws events create-archive \
--archive-name order-archive \
--source-arn arn:aws:events:us-east-1:123456789012:event-bus/orders \
--event-pattern '{"source": ["com.myapp.orders"]}' \
--retention-days 90
# Replay events from a time range
aws events start-replay \
--replay-name reprocess-july \
--event-source-arn arn:aws:events:us-east-1:123456789012:event-bus/orders \
--destination '{"Arn": "arn:aws:events:us-east-1:123456789012:event-bus/orders"}' \
--event-start-time 2026-07-01T00:00:00Z \
--event-end-time 2026-07-02T00:00:00Z
This is invaluable for debugging production issues or reprocessing events after a bug fix.
When to Use Each Service
Use SQS When
- You need reliable point-to-point message delivery
- You want to decouple a producer from a single consumer
- You need to buffer requests during traffic spikes
- Message ordering matters (FIFO queues)
- You want consumers to process at their own pace
Use SNS When
- You need to fan out one message to multiple consumers
- You want push-based delivery (no polling)
- You need to send notifications via email, SMS, or HTTP
- Simple attribute-based filtering is sufficient
Use EventBridge When
- You need content-based routing on complex event structures
- You want to integrate with AWS service events (EC2 state changes, CodePipeline events, etc.)
- You need cross-account or cross-region event routing
- You want event archiving and replay
- You are building an event-driven architecture with many producers and consumers
Combining Services: A Real-World Example
Most architectures use these services together. Here is an order processing system:
# Flow:
# 1. API Gateway -> Lambda (create order)
# 2. Lambda -> EventBridge (publish OrderCreated event)
# 3. EventBridge Rule 1 -> SNS (fan out to notification channels)
# -> SQS (email queue) -> Lambda (send email)
# -> SQS (SMS queue) -> Lambda (send SMS)
# 4. EventBridge Rule 2 -> SQS (inventory queue) -> Lambda (update inventory)
# 5. EventBridge Rule 3 -> Step Functions (fraud check workflow)
Resources:
OrderBus:
Type: AWS::Events::EventBus
Properties:
Name: orders
NotifyCustomerRule:
Type: AWS::Events::Rule
Properties:
EventBusName: !Ref OrderBus
EventPattern:
source: ["com.myapp.orders"]
detail-type: ["OrderCreated", "OrderShipped"]
Targets:
- Id: notification-topic
Arn: !Ref NotificationTopic
InventoryRule:
Type: AWS::Events::Rule
Properties:
EventBusName: !Ref OrderBus
EventPattern:
source: ["com.myapp.orders"]
detail-type: ["OrderCreated"]
Targets:
- Id: inventory-queue
Arn: !GetAtt InventoryQueue.Arn
FraudCheckRule:
Type: AWS::Events::Rule
Properties:
EventBusName: !Ref OrderBus
EventPattern:
source: ["com.myapp.orders"]
detail-type: ["OrderCreated"]
detail:
total: [{"numeric": [">", 200]}]
Targets:
- Id: fraud-check
Arn: !Ref FraudCheckStateMachine
RoleArn: !GetAtt EventBridgeRole.Arn
EventBridge handles the initial routing. SNS fans out notifications to email and SMS queues. SQS ensures reliable processing with DLQs for failed messages. Each service plays to its strengths.
Pricing Comparison
SQS:
Standard: $0.40 per million requests
FIFO: $0.50 per million requests
First million requests/month: Free
SNS:
Publishes: $0.50 per million
SQS deliveries: Free
Lambda deliveries: Free
HTTP deliveries: $0.60 per million
SMS: Varies by country
EventBridge:
Custom events: $1.00 per million
AWS service events: Free
Archive: $0.10 per GB
Replay: $0.10 per million events
EventBridge is 2x the cost of SNS for custom events, but the advanced filtering, routing, and replay capabilities often justify the cost for complex architectures.
Wrapping Up
SQS, SNS, and EventBridge each solve distinct messaging problems. SQS is your go-to for reliable point-to-point queuing with buffering and retry. SNS excels at fan-out when multiple consumers need the same message. EventBridge is the most powerful option for content-based routing, AWS service integration, and event-driven architectures. In practice, most production systems use all three together, with EventBridge routing events to SNS topics that fan out to SQS queues where consumers process messages reliably at their own pace.
Related articles
- AWS AWS SQS vs SNS vs EventBridge: Picking the Right Messaging Service
A practical comparison of SQS queues, SNS topics, and EventBridge buses. When to use each, how they combine, and patterns that scale in production.
- AWS AWS SQS Dead-Letter Queues: Catching Poison Messages
Learn how to configure Amazon SQS dead-letter queues (DLQs) to isolate poison messages, debug consumer failures, and protect your workers from infinite retry loops.
- System Design Event-Driven Architecture: The Pragmatic Introduction
What event-driven architecture really gives you, when to choose it, and the operational realities of running asynchronous systems at scale.
- AWS AWS CDK: Infrastructure in TypeScript Getting Started
Get started with AWS CDK using TypeScript. Define cloud infrastructure with constructs, stacks, and deploy real AWS resources with familiar programming patterns.