Skip to content
Codeloom
AWS

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.

·9 min read · By Codeloom
Intermediate 17 min read

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.