← Back to DevBytes

Monitoring SNS: Metrics, Alarms, and Dashboards

Introduction to SNS Monitoring

Amazon Simple Notification Service (SNS) is a fully managed pub/sub messaging service that decouples producers and consumers. While SNS handles message routing, delivery, and retries automatically, monitoring its behavior is essential. SNS publishes native metrics to Amazon CloudWatch, giving you visibility into message volumes, delivery success rates, and failure patterns. By combining these metrics with CloudWatch Alarms and Dashboards, you build a comprehensive observability layer that surfaces anomalies before they impact downstream systems.

Monitoring SNS means tracking three interconnected layers: metrics—the raw numerical data points that describe your topics and subscriptions; alarms—threshold-based rules that trigger notifications or automated actions when metrics deviate; and dashboards—visual canvases that aggregate graphs, alarms, and key indicators into a single pane of glass. This tutorial walks through each layer with practical code examples you can run against your own AWS account.

Why SNS Monitoring Matters

SNS often sits in the critical path of event-driven architectures. A silent failure in an SNS topic can mean dropped orders, missed alerts, or stalled workflows. Monitoring matters for several concrete reasons:

Key SNS Metrics in CloudWatch

SNS emits metrics in the AWS/SNS namespace. Some metrics apply to the entire topic, while others are scoped to individual subscriptions. Understanding the distinction is crucial for building precise alarms.

Topic-Level Metrics

Topic-level metrics aggregate across all subscriptions and provide a high-level view of publish activity. The most important ones are:

These metrics are available in 1-minute granularity by default. You can enable detailed monitoring for 1-second granularity at a higher cost.

Subscription-Level Metrics

Subscription metrics are published under the same namespace but include a SubscriptionId dimension. They let you pinpoint exactly which subscriber is experiencing problems:

To retrieve subscription-level metrics, you need the subscription ARN or the automatically assigned subscription ID. You can list subscriptions via the SNS API or the AWS CLI.

Setting Up CloudWatch Alarms for SNS

Alarms evaluate a metric against a threshold over a specified number of evaluation periods. When the condition is met, the alarm transitions to ALARM state and can publish to an SNS topic (creating a "meta-alarm" pattern) or invoke actions via composite alarms. Below are practical examples for common SNS alarm scenarios.

Alarm 1: Detect Delivery Failure Rate Spike

This alarm triggers when the percentage of failed deliveries exceeds 5% over a 5-minute window, sustained for two consecutive periods.

aws cloudwatch put-metric-alarm \
  --alarm-name "sns-high-delivery-failure-rate" \
  --alarm-description "Triggers when SNS delivery failure rate exceeds 5%" \
  --namespace "AWS/SNS" \
  --alarm-actions "arn:aws:sns:us-east-1:123456789012:ops-alerts" \
  --metric-name "NumberOfNotificationsFailed" \
  --statistic "Sum" \
  --period 300 \
  --evaluation-periods 2 \
  --threshold 0 \
  --comparison-operator "GreaterThanThreshold" \
  --treat-missing-data "notBreaching" \
  --dimensions "Name=TopicName,Value=order-events" \
  --unit "Count"

For a rate-based alarm, combine two metrics using CloudWatch Metric Math. The CLI supports metric math via the --metrics parameter:

aws cloudwatch put-metric-alarm \
  --alarm-name "sns-failure-rate-percent" \
  --alarm-description "Alert when failed deliveries exceed 5% of total deliveries" \
  --namespace "AWS/SNS" \
  --alarm-actions "arn:aws:sns:us-east-1:123456789012:ops-alerts" \
  --evaluation-periods 2 \
  --threshold 5 \
  --comparison-operator "GreaterThanThreshold" \
  --treat-missing-data "notBreaching" \
  --metrics '[{
    "Id": "failed",
    "MetricStat": {
      "Metric": { "Namespace": "AWS/SNS", "MetricName": "NumberOfNotificationsFailed", "Dimensions": [{ "Name": "TopicName", "Value": "order-events" }] },
      "Stat": "Sum",
      "Period": 300
    },
    "ReturnData": false
  }, {
    "Id": "delivered",
    "MetricStat": {
      "Metric": { "Namespace": "AWS/SNS", "MetricName": "NumberOfNotificationsDelivered", "Dimensions": [{ "Name": "TopicName", "Value": "order-events" }] },
      "Stat": "Sum",
      "Period": 300
    },
    "ReturnData": false
  }, {
    "Id": "percent",
    "Expression": "(failed / (failed + delivered)) * 100",
    "Label": "FailureRatePercent",
    "ReturnData": true
  }]'

Alarm 2: Low Message Volume (Potential Producer Outage)

Detect a sudden drop in published messages—indicating an upstream producer may be down:

aws cloudwatch put-metric-alarm \
  --alarm-name "sns-low-publish-volume" \
  --alarm-description "Triggers when messages published drop below 10 per minute" \
  --namespace "AWS/SNS" \
  --alarm-actions "arn:aws:sns:us-east-1:123456789012:ops-alerts" \
  --metric-name "NumberOfMessagesPublished" \
  --statistic "Sum" \
  --period 60 \
  --evaluation-periods 5 \
  --threshold 10 \
  --comparison-operator "LessThanThreshold" \
  --treat-missing-data "breaching" \
  --dimensions "Name=TopicName,Value=order-events"

Note the use of --treat-missing-data "breaching"—when a producer stops entirely, CloudWatch may see no data points, and this setting ensures the alarm fires rather than staying green.

Alarm 3: Subscription-Level Dead Letter Queue Growth

For Lambda subscriptions that push failures to a dead letter queue (DLQ) via an SQS queue, monitor the queue's ApproximateNumberOfMessagesVisible. While this metric lives in the AWS/SQS namespace, it directly reflects SNS delivery health:

aws cloudwatch put-metric-alarm \
  --alarm-name "sns-dlq-backlog" \
  --alarm-description "DLQ for SNS subscription is growing" \
  --namespace "AWS/SQS" \
  --metric-name "ApproximateNumberOfMessagesVisible" \
  --statistic "Average" \
  --period 300 \
  --evaluation-periods 3 \
  --threshold 50 \
  --comparison-operator "GreaterThanThreshold" \
  --treat-missing-data "notBreaching" \
  --dimensions "Name=QueueName,Value=sns-order-events-dlq"

Alarm 4: Composite Alarm for Multi-Condition Alerting

Composite alarms let you combine multiple metrics into a single alarm without writing complex metric math. For example, alert only if both failure rate is high and publish volume is normal (indicating a real delivery problem rather than a transient zero-volume state):

aws cloudwatch put-composite-alarm \
  --alarm-name "sns-critical-delivery-issue" \
  --alarm-description "High failure rate AND normal publish volume" \
  --alarm-actions "arn:aws:sns:us-east-1:123456789012:pager-alerts" \
  --alarm-rule "ALARM(sns-high-delivery-failure-rate) AND OK(sns-low-publish-volume)"

Composite alarms reference existing metric alarms by name. The rule syntax supports ALARM(), OK(), and INSUFFICIENT_DATA() functions combined with AND, OR, and NOT.

Building Dashboards for SNS

CloudWatch Dashboards provide a customizable visual interface. A well-designed SNS dashboard brings together publish rates, delivery success/failure breakdowns, per-subscription health, and alarm status widgets. Dashboards are defined as JSON and can be created via the AWS CLI, CloudFormation, or the console.

Dashboard JSON Structure

A dashboard widget can display metrics, alarms, logs, or text. For SNS, the most useful widgets are metric widgets showing line graphs and stacked area charts. Below is a complete dashboard body JSON you can use:

{
  "widgets": [
    {
      "type": "metric",
      "x": 0,
      "y": 0,
      "width": 12,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [ "AWS/SNS", "NumberOfMessagesPublished", { "stat": "Sum", "period": 60 }, { "label": "Published" } ],
          [ ".", "NumberOfNotificationsDelivered", { "stat": "Sum", "period": 60 }, { "label": "Delivered" } ],
          [ ".", "NumberOfNotificationsFailed", { "stat": "Sum", "period": 60 }, { "label": "Failed" } ]
        ],
        "region": "us-east-1",
        "title": "SNS Topic: order-events — Throughput",
        "period": 300,
        "stat": "Sum"
      }
    },
    {
      "type": "metric",
      "x": 12,
      "y": 0,
      "width": 12,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          {
            "expression": "(failed / (failed + delivered)) * 100",
            "label": "FailureRatePercent",
            "id": "e1",
            "region": "us-east-1"
          },
          [ "AWS/SNS", "NumberOfNotificationsFailed", { "stat": "Sum", "period": 300, "id": "failed", "visible": false } ],
          [ ".", "NumberOfNotificationsDelivered", { "stat": "Sum", "period": 300, "id": "delivered", "visible": false } ]
        ],
        "region": "us-east-1",
        "title": "SNS Topic: order-events — Failure Rate %",
        "period": 300
      }
    },
    {
      "type": "metric",
      "x": 0,
      "y": 6,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [ "AWS/SNS", "NumberOfNotificationsDelivered", "SubscriptionId", "abc123-def456", { "stat": "Sum", "period": 60, "label": "Lambda-sub" } ],
          [ ".", "NumberOfNotificationsFailed", "SubscriptionId", "abc123-def456", { "stat": "Sum", "period": 60, "label": "Lambda-sub-failures" } ],
          [ ".", "NumberOfNotificationsDelivered", "SubscriptionId", "ghi789-jkl012", { "stat": "Sum", "period": 60, "label": "Email-sub" } ],
          [ ".", "NumberOfNotificationsFailed", "SubscriptionId", "ghi789-jkl012", { "stat": "Sum", "period": 60, "label": "Email-sub-failures" } ]
        ],
        "region": "us-east-1",
        "title": "Per-Subscription Delivery Breakdown",
        "period": 300
      }
    },
    {
      "type": "metric",
      "x": 8,
      "y": 6,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [ "AWS/SNS", "PublishSize", { "stat": "Average", "period": 300, "label": "AvgPublishSizeBytes" } ],
          [ ".", "PublishSize", { "stat": "p90", "period": 300, "label": "P90PublishSizeBytes" } ]
        ],
        "region": "us-east-1",
        "title": "SNS Topic: order-events — Publish Size",
        "period": 300
      }
    },
    {
      "type": "alarm",
      "x": 16,
      "y": 6,
      "width": 8,
      "height": 6,
      "properties": {
        "title": "Active Alarms",
        "region": "us-east-1",
        "alarms": [
          "arn:aws:cloudwatch:us-east-1:123456789012:alarm:sns-high-delivery-failure-rate",
          "arn:aws:cloudwatch:us-east-1:123456789012:alarm:sns-low-publish-volume",
          "arn:aws:cloudwatch:us-east-1:123456789012:alarm:sns-dlq-backlog"
        ]
      }
    }
  ]
}

Creating the Dashboard via CLI

aws cloudwatch put-dashboard \
  --dashboard-name "SNS-Monitoring" \
  --dashboard-body file://sns-dashboard.json

After creation, the dashboard appears in the CloudWatch console under Dashboards > SNS-Monitoring. You can update it at any time with a new body, and CloudWatch handles versioning automatically.

Adding Dashboard to CloudFormation / CDK

For infrastructure-as-code teams, embed the dashboard directly in a CloudFormation template:

Resources:
  SnsDashboard:
    Type: AWS::CloudWatch::Dashboard
    Properties:
      DashboardName: SNS-Monitoring
      DashboardBody: |
        {
          "widgets": [
            {
              "type": "metric",
              "x": 0, "y": 0, "width": 12, "height": 6,
              "properties": {
                "view": "timeSeries",
                "stacked": false,
                "metrics": [
                  [ "AWS/SNS", "NumberOfMessagesPublished", { "stat": "Sum", "period": 60 } ],
                  [ ".", "NumberOfNotificationsDelivered", { "stat": "Sum", "period": 60 } ],
                  [ ".", "NumberOfNotificationsFailed", { "stat": "Sum", "period": 60 } ]
                ],
                "region": "us-east-1",
                "title": "SNS Topic Throughput",
                "period": 300
              }
            }
          ]
        }

With the AWS CDK, use the aws-cloudwatch and aws-cloudwatch-actions libraries to programmatically define dashboards, alarms, and metric graphs in TypeScript or Python. The CDK offers high-level constructs like Dashboard, GraphWidget, and AlarmWidget that compile to the same JSON structure.

Best Practices for SNS Monitoring

Conclusion

Monitoring SNS with CloudWatch Metrics, Alarms, and Dashboards transforms a black-box pub/sub service into a transparent, accountable component of your architecture. Topic-level metrics give you a macro view of throughput and delivery success; subscription-level metrics let you zoom into individual consumer health; alarms surface anomalies before they cascade; and dashboards tie everything into a single operational view. By applying the patterns in this tutorial—rate-based alarms, composite rules, DLQ monitoring, and infrastructure-as-code dashboard definitions—you build a resilient messaging layer that alerts you proactively rather than leaving failures to be discovered by your users. Start with a single dashboard for your busiest topic, iterate on the alarm thresholds as traffic patterns stabilize, and scale the approach across every SNS topic in your system.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles