← Back to DevBytes

Monitoring DocumentDB: Metrics, Alarms, and Dashboards

Introduction to Amazon DocumentDB Monitoring

Amazon DocumentDB (with MongoDB compatibility) is a fully managed, highly durable database service designed for mission-critical workloads. While AWS handles the heavy lifting of infrastructure management, you remain responsible for understanding and optimizing how your cluster behaves under real-world conditions. Monitoring is the bridge between a black-box managed service and a transparent, predictable component of your application stack.

DocumentDB exposes a rich set of metrics through Amazon CloudWatch, giving you visibility into compute utilization, storage performance, query throughput, and replication health. By combining these metrics with alarms and custom dashboards, you gain the ability to detect anomalies early, prevent performance degradation, and make data-driven decisions about scaling or tuning.

This tutorial walks you through the entire monitoring workflow: understanding which metrics matter, configuring alarms that alert you before problems impact users, and building dashboards that give your team a single pane of glass into cluster health. Every concept is accompanied by practical code examples you can adapt directly for your own environment.

What is DocumentDB Monitoring?

DocumentDB monitoring refers to the continuous collection, visualization, and alerting on operational data emitted by your DocumentDB clusters and instances. This data flows into CloudWatch as time-series metrics, which you can then query, graph, and use to trigger automated responses.

The monitoring surface covers three distinct layers:

All metrics are available at a default one-minute granularity, with the option to enable one-second "enhanced monitoring" on individual instances for high-frequency diagnostics. This flexibility lets you balance cost against the level of detail your use case demands.

Why Monitoring Matters

Managed services create a subtle trap: because AWS handles replication, failover, and backups, teams sometimes assume the database "just works." In practice, application workloads evolve, data volumes grow, and query patterns shift. Without monitoring, you only discover problems when users report slowness or when a failover catches you off guard during peak traffic.

Here are the concrete risks that a solid monitoring practice mitigates:

Ultimately, monitoring transforms your DocumentDB deployment from a cost center you trust blindly into an observable asset you actively manage. The time you invest in instrumentation pays for itself many times over during incident response and capacity planning.

Key DocumentDB Metrics Explained

DocumentDB publishes over 30 metrics to CloudWatch. Rather than memorizing them all, focus on the ones that directly signal health, performance, and cost. Below is a curated taxonomy with practical interpretation guidance for each metric.

Compute and Throughput Metrics

These metrics tell you whether your instances have enough horsepower to handle current workload demands.

Query Performance Metrics

These metrics help you understand how efficiently queries are being served, particularly whether indexes are being used correctly.

Replication Health Metrics

These metrics are critical for read-heavy applications that route queries to secondaries.

Storage Metrics

Setting Up Monitoring via AWS CLI

Before building alarms and dashboards, you need to confirm that your cluster is emitting metrics and that you know how to retrieve them programmatically. The following examples use the AWS CLI, but the same principles apply to the SDKs and CloudFormation.

Verifying Metric Streams

First, list the metrics currently available for your cluster. Replace your-cluster-id with your actual cluster identifier.

# List all DocumentDB metrics for a specific cluster
aws cloudwatch list-metrics \
  --namespace "AWS/DocDB" \
  --dimensions Name=DBClusterIdentifier,Value=your-cluster-id \
  --output table

This returns a table of metric names and their dimensions. You'll see entries like:

---------------------------------------------------------------------
|                      ListMetrics Output                         |
---------------------------------------------------------------------
|  MetricName          |  Dimensions                              |
---------------------------------------------------------------------
|  CPUUtilization      |  DBClusterIdentifier, Role, InstanceId   |
|  DatabaseConnections |  DBClusterIdentifier, Role, InstanceId   |
|  QueriesPerSecond    |  DBClusterIdentifier                     |
|  VolumeBytesUsed     |  DBClusterIdentifier                     |
---------------------------------------------------------------------

Notice that some metrics (like QueriesPerSecond) are cluster-scoped, while others (like CPUUtilization) also carry Role and InstanceId dimensions, letting you drill down to individual nodes.

Retrieving Metric Statistics

To fetch the actual time-series data, use get-metric-statistics. The following command retrieves the average CPU utilization across the cluster over the past hour:

# Get average CPU utilization for the last hour (60 periods of 60 seconds)
aws cloudwatch get-metric-statistics \
  --namespace "AWS/DocDB" \
  --metric-name "CPUUtilization" \
  --dimensions Name=DBClusterIdentifier,Value=your-cluster-id \
  --start-time "$(date -u -d '1 hour ago' '+%Y-%m-%dT%H:%M:%SZ')" \
  --end-time "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
  --period 60 \
  --statistics Average \
  --output table

The response contains timestamped data points you can parse programmatically:

---------------------------------------------------------------------
|                  GetMetricStatistics Output                      |
---------------------------------------------------------------------
|  Timestamp              |  Average   |  Unit                      |
---------------------------------------------------------------------
|  2024-01-15 14:00:00   |  12.5      |  Percent                   |
|  2024-01-15 14:01:00   |  14.2      |  Percent                   |
|  2024-01-15 14:02:00   |  11.8      |  Percent                   |
---------------------------------------------------------------------

Enabling Enhanced Monitoring (1-Second Granularity)

By default, DocumentDB metrics are collected every 60 seconds. For latency-sensitive applications, you can enable enhanced monitoring at the instance level, which pushes metrics at 1-second intervals. Enable it when modifying an instance:

# Enable enhanced monitoring on an instance
aws docdb modify-db-instance \
  --db-instance-identifier your-instance-id \
  --monitoring-interval 1 \
  --enable-performance-insights

Note that enhanced monitoring incurs additional CloudWatch charges because of the higher metric ingestion rate. Use it selectively on instances where sub-minute precision genuinely improves your incident response.

Creating CloudWatch Alarms

Alarms are the reactive layer of your monitoring strategy. A well-configured alarm detects an impending problem and notifies youβ€”or triggers automated remediationβ€”before users are affected. The key is setting thresholds that are sensitive enough to catch real issues but not so sensitive that they generate noise.

Alarm Anatomy

Every CloudWatch alarm requires these components:

CPU Utilization Alarm (CLI Example)

The following creates an alarm that fires when cluster CPU utilization exceeds 80% for 3 consecutive minutes, sending a notification to an SNS topic:

# First, create an SNS topic for alarm notifications
TOPIC_ARN=$(aws sns create-topic \
  --name "docdb-production-alerts" \
  --output text \
  --query 'TopicArn')

# Subscribe your email to the topic
aws sns subscribe \
  --topic-arn "$TOPIC_ARN" \
  --protocol email \
  --notification-endpoint "ops-team@example.com"

# Now create the CPU alarm
aws cloudwatch put-metric-alarm \
  --alarm-name "docdb-cluster-high-cpu" \
  --alarm-description "Triggers when cluster CPU exceeds 80% for 3 minutes" \
  --namespace "AWS/DocDB" \
  --metric-name "CPUUtilization" \
  --dimensions Name=DBClusterIdentifier,Value=your-cluster-id \
  --statistic "Average" \
  --period 60 \
  --evaluation-periods 3 \
  --threshold 80 \
  --comparison-operator "GreaterThanThreshold" \
  --alarm-actions "$TOPIC_ARN" \
  --treat-missing-data "notBreaching" \
  --tags Environment=Production,Team=Ops

Important detail: --treat-missing-data "notBreaching" prevents false alarms during brief metric collection gaps. For metrics where missing data itself signals a problem (like a stopped instance), use "breaching" instead.

Replica Lag Alarm

Replica lag is per-instance, so you must include the DBInstanceIdentifier dimension. This alarm fires when any secondary lags more than 500ms for 2 consecutive periods:

aws cloudwatch put-metric-alarm \
  --alarm-name "docdb-secondary-replica-lag" \
  --alarm-description "Replica lag exceeds 500ms on secondary instance" \
  --namespace "AWS/DocDB" \
  --metric-name "ReplicaLag" \
  --dimensions Name=DBInstanceIdentifier,Value=your-secondary-instance-id \
  --statistic "Maximum" \
  --period 300 \
  --evaluation-periods 2 \
  --threshold 500 \
  --comparison-operator "GreaterThanThreshold" \
  --alarm-actions "$TOPIC_ARN" \
  --treat-missing-data "notBreaching"

Using Maximum as the statistic catches spikes that Average might smooth out. A 5-minute period with 2 evaluation periods means the alarm fires after 10 minutes of sustained lagβ€”a reasonable balance between responsiveness and noise suppression.

Freeable Memory Alarm (Trend-Based)

Freeable memory declines gradually as your working set grows. An alarm on the raw value might fire too late. Instead, set a threshold at an absolute value that represents danger for your instance class:

# For an r6g.large with 16 GB total memory, alert when freeable drops below 1.6 GB (10%)
aws cloudwatch put-metric-alarm \
  --alarm-name "docdb-low-freeable-memory" \
  --alarm-description "Freeable memory below 10% of instance capacity" \
  --namespace "AWS/DocDB" \
  --metric-name "FreeableMemory" \
  --dimensions Name=DBInstanceIdentifier,Value=your-instance-id \
  --statistic "Minimum" \
  --period 300 \
  --evaluation-periods 3 \
  --threshold 1717986918 \
  --comparison-operator "LessThanThreshold" \
  --alarm-actions "$TOPIC_ARN" \
  --treat-missing-data "breaching"

Note the threshold value in bytes: 1.6 GB = 1,717,986,918 bytes. Using Minimum over 15 minutes ensures transient dips from cache eviction don't cause false positives.

Volume Growth Rate Alarm (Metric Math)

Sometimes you need to alarm on a derived metric rather than a raw value. CloudWatch Metric Math lets you compute growth rates. The following alarm detects when storage grows by more than 5% over a 24-hour window:

aws cloudwatch put-metric-alarm \
  --alarm-name "docdb-storage-growth-rate" \
  --alarm-description "Storage volume grew more than 5% in 24 hours" \
  --namespace "AWS/DocDB" \
  --metrics '[{
    "Id": "current_volume",
    "MetricStat": {
      "Metric": { "MetricName": "VolumeBytesUsed", "Namespace": "AWS/DocDB" },
      "Period": 86400,
      "Stat": "Average"
    },
    "ReturnData": false
  }, {
    "Id": "previous_volume",
    "MetricStat": {
      "Metric": { "MetricName": "VolumeBytesUsed", "Namespace": "AWS/DocDB" },
      "Period": 86400,
      "Stat": "Average"
    },
    "ReturnData": false
  }, {
    "Id": "growth_pct",
    "Expression": "((current_volume - previous_volume) / previous_volume) * 100",
    "ReturnData": true
  }]' \
  --evaluation-periods 1 \
  --threshold 5 \
  --comparison-operator "GreaterThanThreshold" \
  --alarm-actions "$TOPIC_ARN" \
  --treat-missing-data "notBreaching"

Metric math is powerful but requires careful dimension alignment. Both metrics must share the same dimensions for the expression to evaluate correctly.

Composite Alarms for Noise Reduction

Individual alarms can generate alert storms during cascading failures. Composite alarms combine multiple alarm states into a single notification, reducing noise. For example, notify only when both CPU is high and query throughput exceeds a threshold:

# Create a composite alarm that requires both conditions
aws cloudwatch put-composite-alarm \
  --alarm-name "docdb-overload-composite" \
  --alarm-description "Fires only when CPU high AND query rate elevated" \
  --alarm-rule "ALARM(docdb-cluster-high-cpu) AND ALARM(docdb-high-query-rate)" \
  --alarm-actions "$TOPIC_ARN"

This ensures you're paged only for meaningful incidents, not isolated metric fluctuations.

Building CloudWatch Dashboards

Alarms tell you when something is wrong; dashboards tell you what is happening right now and how trends are evolving. A well-designed dashboard reduces mean time to diagnosis by putting correlated metrics in visual proximity.

Dashboard Structure

CloudWatch dashboards are JSON-defined layouts of widgets. Each widget can display metrics, alarms, logs insights queries, or static text. The dashboard body is an array of widget objects, each with position coordinates and content definitions.

Below is a complete dashboard JSON that covers all critical DocumentDB metrics in a single view. You can deploy it via the CLI or paste it directly into the CloudWatch console.

{
  "widgets": [
    {
      "type": "metric",
      "x": 0,
      "y": 0,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [ "AWS/DocDB", "CPUUtilization", { "stat": "Average", "period": 60 } ]
        ],
        "region": "us-east-1",
        "title": "CPU Utilization (Cluster Average)",
        "yAxis": { "left": { "min": 0, "max": 100, "label": "Percent" } },
        "period": 300
      }
    },
    {
      "type": "metric",
      "x": 8,
      "y": 0,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [ "AWS/DocDB", "DatabaseConnections", { "stat": "Average", "period": 60 } ],
          [ "AWS/DocDB", "QueriesPerSecond", { "stat": "Sum", "period": 60 } ]
        ],
        "region": "us-east-1",
        "title": "Connections and Query Throughput",
        "period": 300
      }
    },
    {
      "type": "metric",
      "x": 16,
      "y": 0,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [ "AWS/DocDB", "ReadIOPS", { "stat": "Sum", "period": 60 } ],
          [ "AWS/DocDB", "WriteIOPS", { "stat": "Sum", "period": 60 } ]
        ],
        "region": "us-east-1",
        "title": "Disk IOPS (Read/Write)",
        "period": 300
      }
    },
    {
      "type": "metric",
      "x": 0,
      "y": 6,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [ "AWS/DocDB", "FreeableMemory", { "stat": "Average", "period": 60 } ],
          [ "AWS/DocDB", "IndexHitRatio", { "stat": "Average", "period": 60 } ]
        ],
        "region": "us-east-1",
        "title": "Memory and Cache Efficiency",
        "period": 300
      }
    },
    {
      "type": "metric",
      "x": 8,
      "y": 6,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [ "AWS/DocDB", "OpcountersQuery", { "stat": "Sum", "period": 60 } ],
          [ "AWS/DocDB", "OpcountersInsert", { "stat": "Sum", "period": 60 } ],
          [ "AWS/DocDB", "OpcountersUpdate", { "stat": "Sum", "period": 60 } ],
          [ "AWS/DocDB", "OpcountersDelete", { "stat": "Sum", "period": 60 } ]
        ],
        "region": "us-east-1",
        "title": "Operation Counts per Second",
        "period": 300
      }
    },
    {
      "type": "metric",
      "x": 16,
      "y": 6,
      "width": 8,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [ "AWS/DocDB", "ReplicaLag", { "stat": "Maximum", "period": 60 } ],
          [ "AWS/DocDB", "SlowQueriesPerSecond", { "stat": "Sum", "period": 60 } ]
        ],
        "region": "us-east-1",
        "title": "Replication Lag and Slow Queries",
        "period": 300
      }
    },
    {
      "type": "metric",
      "x": 0,
      "y": 12,
      "width": 12,
      "height": 6,
      "properties": {
        "view": "timeSeries",
        "stacked": false,
        "metrics": [
          [ "AWS/DocDB", "VolumeBytesUsed", { "stat": "Average", "period": 3600 } ]
        ],
        "region": "us-east-1",
        "title": "Storage Volume Growth (Hourly Samples)",
        "period": 3600
      }
    },
    {
      "type": "alarm",
      "x": 12,
      "y": 12,
      "width": 12,
      "height": 6,
      "properties": {
        "title": "Active Alarms",
        "alarms": [
          "docdb-cluster-high-cpu",
          "docdb-secondary-replica-lag",
          "docdb-low-freeable-memory",
          "docdb-storage-growth-rate"
        ]
      }
    },
    {
      "type": "log",
      "x": 0,
      "y": 18,
      "width": 24,
      "height": 8,
      "properties": {
        "region": "us-east-1",
        "title": "Slow Query Log Insights",
        "query": "fields @timestamp, query, duration, ns\n| filter @logStream like /slow-query/\n| sort @timestamp desc\n| limit 20",
        "view": "table"
      }
    }
  ]
}

Deploying the Dashboard via CLI

Save the JSON above as docdb-dashboard.json and deploy it with a single command:

# Create the dashboard (or update if it already exists)
aws cloudwatch put-dashboard \
  --dashboard-name "DocumentDB-Production-Overview" \
  --dashboard-body file://docdb-dashboard.json

To update an existing dashboard, simply run the same command with modified JSON. CloudWatch replaces the dashboard body atomically. You can also retrieve the current dashboard body for editing:

# Get current dashboard definition
aws cloudwatch get-dashboard \
  --dashboard-name "DocumentDB-Production-Overview" \
  --output text \
  --query 'DashboardBody' > current-dashboard.json

Per-Instance Drill-Down Dashboard

The cluster-level dashboard above gives a broad view, but when you need to identify which replica is struggling, you need per-instance metrics. The following snippet shows how to structure a widget that breaks down CPU by instance:

{
  "type": "metric",
  "x": 0,
  "y": 0,
  "width": 24,
  "height": 6,
  "properties": {
    "view": "timeSeries",
    "stacked": false,
    "metrics": [
      [ "AWS/DocDB", "CPUUtilization", "DBInstanceIdentifier", "your-primary-id", { "stat": "Average", "period": 60, "label": "Primary" } ],
      [ "AWS/DocDB", "CPUUtilization", "DBInstanceIdentifier", "your-secondary-1-id", { "stat": "Average", "period": 60, "label": "Secondary-1" } ],
      [ "AWS/DocDB", "CPUUtilization", "DBInstanceIdentifier", "your-secondary-2-id", { "stat": "Average", "period": 60, "label": "Secondary-2" } ]
    ],
    "region": "us-east-1",
    "title": "CPU Utilization by Instance",
    "period": 300
  }
}

Notice the explicit DBInstanceIdentifier dimension in each metric array element. Without it, CloudWatch aggregates across all instances, masking individual outliers.

Automating Alarm and Dashboard Deployment

Manually creating alarms and dashboards works for small deployments, but production environments benefit from infrastructure-as-code approaches. Here are patterns for CloudFormation and the AWS CDK.

CloudFormation Template (Alarm Snippet)

Below is a CloudFormation snippet that creates the CPU alarm and its SNS topic. You can embed this in a larger template alongside your DocumentDB cluster definition:

Resources:
  DocDBAlertTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: docdb-production-alerts
      Subscription:
        - Endpoint: ops-team@example.com
          Protocol: email

  DocDBHighCPUAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: docdb-cluster-high-cpu
      AlarmDescription: "Triggers when cluster CPU exceeds 80% for 3 minutes"
      Namespace: "AWS/DocDB"
      MetricName: "CPUUtilization"
      Dimensions:
        - Name: DBClusterIdentifier
          Value: !Ref DocDBCluster
      Statistic: Average
      Period: 60
      EvaluationPeriods: 3
      Threshold: 80
      ComparisonOperator: GreaterThanThreshold
      AlarmActions:
        - !Ref DocDBAlertTopic
      TreatMissingData: notBreaching

CDK Example (TypeScript)

For teams using the AWS CDK, here's a reusable construct that creates a DocumentDB monitoring stack:

import * as cdk from 'aws-cdk-lib';
import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as snsSubscriptions from 'aws-cdk-lib/aws-sns-subscriptions';

export class DocDBMonitoringStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props: cdk.StackProps & { clusterId: string }) {
    super(scope, id, props);

    // SNS topic for all DocumentDB alerts
    const alertTopic = new sns.Topic(this, 'DocDBAlertTopic', {
      topicName: 'docdb-production-alerts',
    });
    alertTopic.addSubscription(new snsSubscriptions.EmailSubscription('ops-team@example.com'));

    // CPU utilization alarm
    new cloudwatch.Alarm(this, 'DocDBHighCPUAlarm', {
      alarmName: 'docdb-cluster-high-cpu',
      alarmDescription: 'Triggers when cluster CPU exceeds 80% for 3 minutes',
      metric: new cloudwatch.Metric({
        namespace: 'AWS/DocDB',
        metricName: 'CPUUtilization',
        dimensionsMap: { DBClusterIdentifier: props.clusterId },
        statistic: 'Average',
        period: cdk.Duration.seconds(60),
      }),
      threshold: 80,
      evaluationPeriods: 3,
      comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
      treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
    }).addAlarmAction(new cloudwatch.SnsAlarmAction(alertTopic));

    // Replica lag alarm
    new cloudwatch.Alarm(this, 'DocDBReplicaLagAlarm', {
      alarmName: 'docdb-secondary-replica-lag',
      alarmDescription: 'Replica lag exceeds 500ms on secondary',
      metric: new cloudwatch.Metric({
        namespace: 'AWS/DocDB',
        metricName: 'ReplicaLag',
        dimensionsMap: {
          DBInstanceIdentifier: 'your-secondary-instance-id',
        },
        statistic: 'Maximum',
        period: cdk.Duration.seconds(300),
      }),
      threshold: 500,
      evaluationPeriods: 2,
      comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
      treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
    }).addAlarmAction(new cloudwatch.SnsAlarmAction(alertTopic));

    // Dashboard
    const dashboard = new cloudwatch.Dashboard(this, 'DocDBDashboard', {
      dashboardName: 'DocumentDB-Production-Overview',
    });

    dashboard.addWidgets(
      new cloudwatch.GraphWidget({
        title: 'CPU Utilization (Cluster Average)',
        left: [
          new cloudwatch.Metric({
            namespace: 'AWS/DocDB',
            metricName: 'CPUUtilization',
            dimensionsMap: { DBClusterIdentifier: props.clusterId },
            statistic: 'Average',
            period: cdk.Duration.seconds(300),
          }),
        ],
        width: 8,
        height: 6,
      }),
      new cloudwatch.GraphWidget({
        title: 'Connections and Query Throughput',
        left: [
          new cloudwatch.Metric({
            namespace: 'AWS/DocDB',
            metricName: 'DatabaseConnections',
            dimensionsMap: { DBClusterIdentifier: props.clusterId },
            statistic: 'Average',
            period: cdk.Duration.seconds(300),
          }),
          new cloudwatch.Metric({
            namespace: 'AWS/DocDB',
            metricName: 'QueriesPerSecond',
            dimensionsMap: { DBClusterIdentifier: props.clusterId },
            statistic: 'Sum',
            period: cdk.Duration.seconds(300),
          }),
        ],
        width: 8,
        height: 6,
      }),
      new cloudwatch.AlarmWidget({
        title: 'Active Alarms',
        alarm: cloudwatch.Alarm.fromAlarmArn(
          this,
          'ImportedCPUAlarm',
          'arn:aws:cloudwatch:us-east-1:123456789012:alarm:docdb-cluster-high-cpu'
        ),
        width: 8,
        height: 6,
      })
    );
  }
}

The CDK approach brings type safety and composability. You can parameterize thresholds, instance IDs, and notification endpoints via context values or environment variables, making the same construct reusable across development, staging, and production environments.

Best Practices for

πŸš€ 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