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
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
| Setting | What it does | Typical choice |
|---|---|---|
| Statistic | How points within a period are combined | Sum for counts (Errors, Throttles); Average/Max or a percentile (p99) for Duration |
Period | Seconds 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 pages | 60–300 |
EvaluationPeriods (N) | How many recent periods are considered | 3–5 |
DatapointsToAlarm (M) | How many of those must breach | M < N to ride out one noisy point |
TreatMissingData | missing (default), notBreaching, breaching, ignore | notBreaching 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.
missinggoes toINSUFFICIENT_DATAonly when every point in the range is missing.AWS/DynamoDBalarms default toignore.
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
notBreachingcovers 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
| Type | Use it for |
|---|---|
| Composite | Page 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 alarm | Alarm on the results of a scheduled Logs Insights query, evaluated M of N, for conditions that exist only in logs |
| PromQL alarm | A PromQL instant query over metrics sent to CloudWatch's OTLP endpoint, with pending and recovery durations |
| Metric math across accounts | Supported, except with ANOMALY_DETECTION_BAND, INSIGHT_RULE and SERVICE_QUOTA |
SEARCH expressions | Can't be alarmed on: they return many series |
Alarm stuck or noisy?
| Symptom | Likely cause → fix |
|---|---|
Always INSUFFICIENT_DATA | The 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 errors | TreatMissingData: missing with an error metric that only reports on failures. Or the statistic is Average where Sum was meant |
| Flaps on single spikes | M = N = 1. Use M of N, for example 3 of 5 |
| Fired once, no repeat notifications | Expected: actions run on state change only |
| Action did nothing | The 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) |