Amazon CloudWatch Custom Metrics: A Practical Setup Guide
Published on · Updated on
Replaced stale pricing and limits, corrected metric identity and alarm guidance, and added current OpenTelemetry, IAM, resolution, and troubleshooting details.
Custom metrics let you monitor application behavior that AWS service metrics do not describe, such as completed checkouts or jobs waiting for processing. Before publishing one, decide what action the measurement will support and who will use it.
AWS now recommends OpenTelemetry for new custom-metric implementations. CloudWatch also supports the established Metrics (Classic) model, including PutMetricData, the AWS SDKs and CLI, and metrics extracted from CloudWatch Logs. This guide explains the classic setup because it is useful for existing integrations and simple direct publishing; see CloudWatch OpenTelemetry metrics when choosing an approach for new instrumentation. The models have different querying and pricing, so estimate the path you plan to use with the current CloudWatch pricing page.
- Classic metrics are identified by namespace, metric name, and the complete set of dimensions.
- Use stable, low-cardinality dimensions such as service and environment; do not use request or customer IDs.
- Choose standard or high resolution based on the response time your use case needs.
- Give the publisher only the permissions it needs, and check the metric in the same AWS account and Region where it was sent.
What are CloudWatch custom metrics?
A CloudWatch metric is a time series of timestamped values. In the classic model, its identity is a namespace, metric name, and zero or more dimensions. For example, Acme/Checkout / CheckoutSuccess with Environment=production is a different time series from the same name with Environment=staging. Metrics are regional; publish and query them in the same Region.
AWS services publish their own metrics, while your application can publish a custom metric for a business or operational signal that those metrics do not cover. Keep its meaning and unit consistent: for example, publish successful checkout events as Count values and use the Sum statistic to see how many occurred during a period.
Choose how to publish
Use the path that fits how your application already produces telemetry:
- OpenTelemetry (recommended by AWS for new implementations): instrument with an OpenTelemetry SDK or collector and send metrics over OTLP. CloudWatch's native OpenTelemetry metrics use PromQL and a separate pricing model. Follow the AWS publishing guide for regional endpoints, authentication, and setup.
PutMetricDatathrough an SDK or the AWS CLI: use this classic path when an integration already expects CloudWatch metric names and dimensions or when direct API publishing is the right fit.- Embedded Metric Format (EMF): if an application already sends structured logs to CloudWatch Logs, it can include metric values in those log events for CloudWatch to extract. EMF needs
logs:PutLogEvents; charges can include log ingestion, log storage, and generated classic metrics. AWS notes that extraction can occasionally produce duplicate values, so account for that in the metric's meaning.
For operating-system metrics, files, or supported host telemetry, configure the CloudWatch agent. It is a collection agent, not a requirement for applications that publish through an SDK or OpenTelemetry.
Publish a classic metric with the AWS SDK for Python
This example publishes one successful checkout. The SDK uses the normal AWS credential provider chain, so run it with an IAM role or configured AWS credentials that can publish to this namespace. The default storage resolution is 60 seconds.
from datetime import datetime, timezone
import boto3
cloudwatch = boto3.client("cloudwatch", region_name="us-east-1")
cloudwatch.put_metric_data(
Namespace="Acme/Checkout",
MetricData=[
{
"MetricName": "CheckoutSuccess",
"Dimensions": [
{"Name": "Service", "Value": "checkout"},
{"Name": "Environment", "Value": "production"},
],
"Timestamp": datetime.now(timezone.utc),
"Value": 1,
"Unit": "Count",
}
],
)
Use a namespace that identifies your application and does not begin with the reserved AWS/ prefix. Keep the unit consistent across data points. When publishing many events, batch data points where appropriate; every API request and every distinct classic metric series can affect cost.
Limit the publisher's IAM permission
Attach a publishing permission to the application role that sends the metric. For classic metric publication, AWS shows Resource: "*" and the cloudwatch:namespace condition key to limit which namespace the identity can write to:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "cloudwatch:PutMetricData",
"Resource": "*",
"Condition": {
"StringEquals": {
"cloudwatch:namespace": "Acme/Checkout"
}
}
}
]
}
Use a workload role where available rather than placing long-lived access keys in application code. If the application publishes through EMF, grant the required CloudWatch Logs permissions instead and do not add PutMetricData unless the application also calls that API.
Publish and query with the AWS CLI
The CLI uses the same signed AWS API and your configured AWS credentials:
aws cloudwatch put-metric-data \
--namespace Acme/Checkout \
--metric-name CheckoutSuccess \
--dimensions Service=checkout,Environment=production \
--value 1 \
--unit Count \
--region us-east-1
After publishing a point, query it with the exact namespace, name, dimension set, account, and Region. Replace the example UTC interval with one that includes your published data:
aws cloudwatch get-metric-statistics \
--namespace Acme/Checkout \
--metric-name CheckoutSuccess \
--dimensions Name=Service,Value=checkout Name=Environment,Value=production \
--start-time 2026-10-04T12:00:00Z \
--end-time 2026-10-04T13:00:00Z \
--period 60 \
--statistics Sum \
--region us-east-1
For a counter that publishes one value per event, Sum reports the number of events in each period. For rates, latency, or gauges, select a statistic that matches the metric's meaning. CloudWatch can take up to two minutes to return statistics for a new metric, and up to 15 minutes to show it in ListMetrics.
Dimensions, resolution, and metric math
Keep dimensions bounded
A dimension is a name-and-value pair that distinguishes one metric series from another. A classic metric can have up to 30 dimensions. Each distinct combination creates a separate series, so dimensions are not free-form tags that can be added later to filter one shared series.
Prefer a small set of stable values such as Service, Environment, or a bounded Operation name. Avoid request IDs, user IDs, timestamps, or other values that change for each event: those create a large number of unique series and make data harder to aggregate. When retrieving a metric, specify the complete dimension set used when it was published.
Choose standard or high resolution
Classic metrics use standard resolution by default, with one-minute granularity. Set StorageResolution to 1 only when you need high-resolution data at one-second granularity. You can retrieve high-resolution data with periods of 1, 5, 10, or 30 seconds, or a multiple of 60 seconds. High-resolution alarms support periods of 10, 20, or 30 seconds; standard alarms use periods that are multiples of 60 seconds. More frequent PutMetricData calls can increase API request charges, and high-resolution alarms cost more than standard alarms. Use them when the shorter feedback loop changes an operational decision; see the PutMetricAlarm period requirements.
Retention depends on the period: sub-minute high-resolution data is available for 3 hours; one-minute data for 15 days; five-minute data for 63 days; and one-hour data for 455 days. CloudWatch aggregates older data to coarser periods as it ages.
Calculate from existing metrics
Metric math can combine existing time series into an expression that you can graph or use in an alarm. For example, if a graph contains error count m1 and request count m2, an expression such as 100 * m1 / m2 can show an error percentage. Choose compatible periods and statistics, and handle periods with no requests deliberately. Metric math calculates a view from existing metrics; it does not require publishing another custom metric.
Alarm on a custom metric
Once CloudWatch has received data, create a metric alarm from the metric's namespace, name, and exact dimensions. Choose a statistic that matches the measure (Sum for event counts, for example), a period, a threshold, and how many of the recent evaluation periods must breach it. Decide how the alarm should treat missing data: missing values can mean the application is quiet, or that a continuously reporting publisher has stopped.
Test the alarm against real data before relying on it. Configure a supported action, such as an SNS notification or a Lambda invocation. For Lambda, add a resource-based permission that authorizes CloudWatch to invoke the function; you can also route alarm state changes through EventBridge. For threshold selection, evaluation periods, and alarm behavior, see the site's CloudWatch alarm guide.
EC2 Auto Scaling can use a custom metric in a configured target-tracking policy, but the metric must suit the policy and reflect load or utilization in a way that responds to capacity. Follow the target-tracking metric requirements before using an application metric to scale a group.
Control custom metric costs
For classic metrics, cost depends in part on the number of distinct metric series active, not just the number of metric names. A new dimension combination creates a new series. Publication requests, high-resolution metrics, alarms, dashboards, and queries can also contribute to the bill. EMF adds log ingestion and storage costs as well as charges for the generated classic metrics. OpenTelemetry metrics use a separate CloudWatch pricing model.
Estimate cost from the dimensions your application can actually emit, keep high-cardinality values out of classic metric dimensions, and batch classic publication where practical. Review usage by service and Region in your AWS bill, and check the CloudWatch pricing page before rollout. AWS changes pricing and free usage terms; a fixed price table in a setup guide can go stale quickly.
Troubleshoot missing metrics
| Symptom | What to check |
|---|---|
| The publish call returns access denied | Confirm the caller's role permits cloudwatch:PutMetricData for the namespace in the policy condition. For EMF, verify the CloudWatch Logs permissions instead. |
| The metric is not in the console yet | Confirm the account and Region, then check the exact namespace, name, and dimensions with GetMetricData or GetMetricStatistics. New statistics can take up to two minutes to appear; ListMetrics can take up to 15 minutes. |
| A metric disappears from browsing | Metrics with no new data for two weeks are omitted from console browsing and ListMetrics. This does not mean retained data was deleted; query it by its exact identity within the retention period. |
| A query or alarm has no data | Check that every dimension name and value exactly matches publication, the query uses the correct Region and period, and timestamps are current. Then review the alarm's missing-data setting. |
For definitions, retention, and publication limits, use the current AWS custom metric publishing guide and metric concepts reference.
Frequently asked questions
Do I need to create a custom metric before publishing?
No. Publishing a data point for a namespace, metric name, and dimension set creates that metric series. Continue publishing data when you need fresh points; CloudWatch does not pull custom metrics from your application.
Why can I not find a metric in the CloudWatch console?
Check the account, Region, namespace, name, and every dimension value. A new metric can take up to 15 minutes to appear in metric browsing, and a metric with no new points for two weeks is omitted from browsing. Query a known metric identity directly to look for retained data.
Can a custom metric trigger an AWS Lambda function?
A custom metric can be evaluated by a CloudWatch metric alarm. You can configure the alarm's action to invoke a Lambda function directly; the function needs a resource-based permission that allows CloudWatch to invoke it. Alarm state changes are also sent to EventBridge, where a rule can route them to other targets. Metric publication alone does not invoke a function. See AWS's guides to invoking Lambda from an alarm and acting on alarm changes.