Create CloudWatch alarms that fire when they should

Build alarms that page for real failures and stay quiet otherwise — pick the metric and statistic, set period, evaluation periods and datapoints-to-alarm (M of N), choose TreatMissingData (default missing, notBreaching for error-only metrics), alarm on an error rate with metric math (Lambda Errors / Invocations) in TypeScript SDK and CDK, combine with composite alarms, and know the 2026 log and PromQL alarm types.

markdownguidev4TMJ8-3H3Astage: operatetrust: officialservice: cloudwatchinterface: sdkinterface: iacinterface: cliCloudWatch alarmPutMetricAlarmTreatMissingDatanotBreachingINSUFFICIENT_DATAdatapoints to alarmM out of Nevaluation periodsperiodmetric math alarmerror rate alarm LambdaErrors InvocationsFILLcomposite alarmalarm actions SNSalarm actions only on state changelog alarm Logs InsightsPromQL alarmCDK Alarm MathExpressionalarm not firingalarm flapping

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

Alarm on a rate or a symptom users feel, require M breaching points out of N, and decide on purpose what "no data" means. The default, TreatMissingData: missing, sends a quiet metric to INSUFFICIENT_DATA, which pages nobody. An error-count metric that emits nothing on good days needs notBreaching.

The settings that decide behaviour

SettingWhat it doesTypical choice
StatisticHow points within a period are combinedSum for counts (Errors, Throttles); Average/Max or a percentile (p99) for Duration
PeriodSeconds per data point. Metric periods are 1, 5, 10, 30 or a multiple of 60; sub-minute periods need 1-second custom metrics. Which sub-minute values alarms accept isn't on the fetched pages60–300
EvaluationPeriods (N)How many recent periods are considered3–5
DatapointsToAlarm (M)How many of those must breachM < N to ride out one noisy point
TreatMissingDatamissing (default), notBreaching, breaching, ignorenotBreaching for error-only metrics; missing for EC2 stop, terminate, reboot or recover actions

How missing data is handled:

  • CloudWatch looks a little further back than N periods with the default sliding windows.
  • If it finds at least N real points, the missing-data setting isn't used. Otherwise it fills the gaps with the setting, as few times as possible.
  • missing goes to INSUFFICIENT_DATA only when every point in the range is missing.
  • AWS/DynamoDB alarms default to ignore.

The evaluation window (period × N) can be at most 7 days for periods of an hour or more, and 1 day for shorter periods.

Actions run only when the state changes. An alarm that stays in ALARM doesn't notify again; Auto Scaling actions are the exception and repeat every minute. CloudWatch doesn't check that the SNS topic or other target exists, so test it. Metric alarms can notify SNS, act on EC2 or Auto Scaling, open Systems Manager OpsItems or incidents, and start CloudWatch investigations.

Error rate for a Lambda function (TypeScript SDK)

Errors counts function errors, timeouts included, but not throttles. Invocations excludes throttled requests. So the rate below is "share of runs that failed". Alarm on Throttles separately.

import { CloudWatchClient, PutMetricAlarmCommand } from "@aws-sdk/client-cloudwatch";

const cw = new CloudWatchClient({});
const fn = "orders-handler";
const dims = [{ Name: "FunctionName", Value: fn }];

await cw.send(new PutMetricAlarmCommand({
  AlarmName: `${fn}-error-rate`,
  AlarmDescription: "More than 5% of invocations failed in 3 of 5 minutes",
  ComparisonOperator: "GreaterThanThreshold",
  Threshold: 5,
  EvaluationPeriods: 5,
  DatapointsToAlarm: 3,
  TreatMissingData: "notBreaching",            // no traffic is not an outage
  AlarmActions: ["arn:aws:sns:<region>:<account-id>:oncall"],
  OKActions: ["arn:aws:sns:<region>:<account-id>:oncall"],
  Metrics: [
    { Id: "errors", ReturnData: false,
      MetricStat: { Metric: { Namespace: "AWS/Lambda", MetricName: "Errors", Dimensions: dims }, Period: 60, Stat: "Sum" } },
    { Id: "invocations", ReturnData: false,
      MetricStat: { Metric: { Namespace: "AWS/Lambda", MetricName: "Invocations", Dimensions: dims }, Period: 60, Stat: "Sum" } },
    { Id: "rate", Label: "Error %", ReturnData: true,
      Expression: "100 * FILL(errors, 0) / invocations" },
  ],
}));
  • In metric math, missing values count as 0 in arithmetic, and a point that would divide by zero is dropped. So minutes with no invocations produce no point, and notBreaching covers them.
  • Exactly one entry has ReturnData: true: that's the series the alarm evaluates.
  • IDs start with a lowercase letter, and function names are uppercase.
  • Avoid scalar functions such as SUM(m1) on a single series in alarms.

The same alarm in the CDK:

import { Duration } from "aws-cdk-lib";
import * as cloudwatch from "aws-cdk-lib/aws-cloudwatch";
import type * as lambda from "aws-cdk-lib/aws-lambda";
import type { Construct } from "constructs";

export function errorRateAlarm(scope: Construct, fn: lambda.IFunction) {
  const rate = new cloudwatch.MathExpression({
    expression: "100 * FILL(errors, 0) / invocations",
    usingMetrics: {
      errors: fn.metricErrors({ statistic: "Sum", period: Duration.minutes(1) }),
      invocations: fn.metricInvocations({ statistic: "Sum", period: Duration.minutes(1) }),
    },
    label: "Error %",
  });
  return rate.createAlarm(scope, "ErrorRate", {
    threshold: 5,
    evaluationPeriods: 5,
    datapointsToAlarm: 3,
    comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
    treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
  });
}

Attach a notification with alarm.addAlarmAction(new SnsAction(topic)) from aws-cdk-lib/aws-cloudwatch-actions.

Other alarm types

TypeUse it for
CompositePage only when several alarms agree, for example high error rate AND high latency. A composite can notify SNS, open OpsItems or incidents, and start investigations, but can't take EC2 or Auto Scaling actions. It can't span accounts
Log alarmAlarm on the results of a scheduled Logs Insights query, evaluated M of N, for conditions that exist only in logs
PromQL alarmA PromQL instant query over metrics sent to CloudWatch's OTLP endpoint, with pending and recovery durations
Metric math across accountsSupported, except with ANOMALY_DETECTION_BAND, INSIGHT_RULE and SERVICE_QUOTA
SEARCH expressionsCan't be alarmed on: they return many series

Alarm stuck or noisy?

SymptomLikely cause → fix
Always INSUFFICIENT_DATAThe metric has no data with exactly these dimensions: dimensions are part of a metric's identity. Check with aws cloudwatch list-metrics --namespace … --metric-name …. A brand-new custom metric can take up to 15 minutes to show there
Never fires on errorsTreatMissingData: missing with an error metric that only reports on failures. Or the statistic is Average where Sum was meant
Flaps on single spikesM = N = 1. Use M of N, for example 3 of 5
Fired once, no repeat notificationsExpected: actions run on state change only
Action did nothingThe target ARN is wrong or not permitted, and CloudWatch doesn't validate actions. The SNS topic policy must allow cloudwatch.amazonaws.com SNS:Publish, conditioned on the alarm ARN and account. Email subscriptions must be confirmed (reference/sns/errors.md)

Read this document as markdown — the same bytes an agent receives.