---
cuppy_hub: aws
cuppy_path: guides/cloudwatch/create-alarms.md
cuppy_section: docs
cuppy_doc: TMJ8-3H3A
cuppy_doc_uuid: e7ccdc8a-9c55-4b35-afdf-4a7fe8fa55d5
cuppy_permalink: https://cuppy.io/aws/d/TMJ8-3H3A
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/guides/cloudwatch/create-alarms
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: df523fbda0dfe4f68227aafb6b957bd427d99a7adf9ec7d25bcc218591c6e313
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Create CloudWatch alarms that fire when they should

**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.
- `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.

```ts
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:

```ts
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`) |


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 131 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/aws/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
