CloudWatch Managed Prometheus Collectors: Setup Guide for EC2 and ECS

CloudWatch Managed Prometheus Collectors: Setup Guide for EC2 and ECS

If you've been running your own OpenTelemetry Collector just to get Prometheus metrics into CloudWatch, AWS shipped the fix in late July 2026: managed Prometheus collectors. You hand CloudWatch a scrape config and a VPC connection, and it provisions, scales, and runs the scraper for you โ€” no EC2 instance or Fargate task babysitting a Collector binary. This walks through actually setting one up for EC2 and ECS targets, not just the announcement.

What's actually new here

AWS already had Amazon Managed Service for Prometheus (AMP) scrapers โ€” the aws amp create-scraper API has existed since 2023 for EKS and, later, VPC-based sources. What's new is the destination: a scraper can now write straight into a CloudWatch dataset instead of an AMP workspace. That matters in practice because you skip standing up (and paying for) a separate AMP workspace just to see Prometheus metrics โ€” they land in CloudWatch, queryable with PromQL right alongside your existing EC2, ECS, and RDS metrics, usable in the same dashboards and alarms.

Supported sources at launch: Amazon EKS (Kubernetes service discovery), Amazon EC2 and ECS (VPC-connected, static_configs / dns_sd_configs), and Amazon MSK and OpenSearch Service (open monitoring endpoints). This guide covers the VPC-connected path โ€” EC2 and ECS โ€” since that's where most teams currently have a hand-rolled Collector to retire.

Why this replaces a self-managed Collector

The old way: deploy an ADOT (AWS Distro for OpenTelemetry) Collector on an instance or as a sidecar, write a scrape config, manage its lifecycle, scale it as targets grow, and patch it when CVEs land. The managed collector removes all of that โ€” CloudWatch creates elastic network interfaces (ENIs) in your subnets, runs the scrape loop, and delivers metrics over the AWS network. You still own the scrape config; you just stop owning the process that executes it.

Prerequisites

  • A VPC with DNS resolution enabled
  • At least two subnets in different Availability Zones (the collector creates an ENI per AZ)
  • A security group that allows the collector to reach your exporter ports (9100 for Node Exporter, whatever port your app exposes /metrics on)
  • Targets already exposing a Prometheus-compatible /metrics endpoint
  • If your subnets are private with no NAT/internet route, an interface VPC endpoint for com.amazonaws.{region}.monitoring โ€” without it the collector can scrape but can't deliver metrics to CloudWatch

Step 1: Write a scrape config for EC2

For EC2 instances running Node Exporter, use static_configs with private IPs. Relabeling tags each series so you can tell EC2-sourced metrics apart from everything else once they're mixed into CloudWatch:

 1global:
 2  scrape_interval: 60s
 3
 4scrape_configs:
 5  - job_name: 'ec2-node-exporter'
 6    static_configs:
 7      - targets:
 8          - '10.0.1.10:9100'
 9          - '10.0.1.11:9100'
10    relabel_configs:
11      - source_labels: [__address__]
12        target_label: instance
13      - target_label: compute_platform
14        replacement: 'ec2'

Save this as scrape-config.yaml.

Step 2: Create the scraper via CLI

 1aws amp create-scraper \
 2  --alias "ec2-metrics-scraper" \
 3  --source '{
 4    "vpcConfiguration": {
 5      "subnetIds": ["subnet-0abc123", "subnet-0def456"],
 6      "securityGroupIds": ["sg-0123456789abcdef0"]
 7    }
 8  }' \
 9  --scrape-configuration configurationBlob=$(cat scrape-config.yaml | base64 -w 0) \
10  --destination '{
11    "cloudWatchConfiguration": {
12      "datasetArn": "arn:aws:cloudwatch:us-west-2:123456789012:dataset/default"
13    }
14  }'

The scraper still lives under the amp CLI namespace (it's the same underlying scraper service AMP has used since 2023) โ€” the only thing that changed is pointing destination at a cloudWatchConfiguration dataset ARN instead of an AMP workspace ARN.

Step 3: ECS โ€” use Cloud Map service discovery instead

ECS task IPs churn on every deployment and every scaling event, so static targets don't work. Register your services with AWS Cloud Map and use dns_sd_configs โ€” the scraper queries the Cloud Map DNS records on every scrape interval and picks up new/removed tasks automatically:

 1global:
 2  scrape_interval: 60s
 3
 4scrape_configs:
 5  - job_name: 'ecs-services'
 6    dns_sd_configs:
 7      - names:
 8          - 'my-service.my-namespace.local'
 9        type: A
10        port: 9090
11    relabel_configs:
12      - source_labels: [__meta_dns_name]
13        target_label: service_name
14      - source_labels: [__address__]
15        target_label: instance
16      - target_label: compute_platform
17        replacement: 'ecs'

Same create-scraper call as above, just with this config and a dataset destination scoped to your ECS collector's own alias โ€” don't reuse the EC2 scraper's alias for a second create-scraper call, or you'll overwrite it.

Querying what you just collected

Once metrics land in CloudWatch, they're queryable with PromQL, mixed with your native AWS metrics:

1100 - (avg by (instance, job) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

That's CPU utilization computed from raw Node Exporter counters โ€” something you'd otherwise need CloudWatch agent's own metric set for, now derived directly from the same Prometheus data your app teams already instrument with.

Best practices

  • Deploy in private subnets and rely on the VPC endpoint for delivery โ€” don't put the collector's ENIs in a public subnet just because it's simpler to set up.
  • Restrict security group ingress to only the scraper's security group, on only the exporter ports you actually use. A wide-open 9100 rule defeats the point of running this inside a VPC.
  • Set scrape intervals by workload, not by default. AWS's own guidance: halving your scrape interval doubles ingestion cost. 30s is reasonable for application metrics you alert on; 60s+ is fine for infrastructure metrics; stretch non-prod environments to 90s or more.
  • Drop noisy series before they hit CloudWatch, using metric_relabel_configs with a drop action on high-cardinality or unused metric names โ€” you're billed on ingestion, so filtering at the scrape config is cheaper than filtering after the fact.

Common mistakes to avoid

  • Forgetting the VPC endpoint in private subnets. The collector will scrape targets successfully and still silently fail to deliver metrics if there's no route to the CloudWatch API โ€” this shows up as "the scraper exists but I see no data," not an obvious error.
  • Reusing an AMP workspace scrape config as-is. The scrape config format is identical, but if you're migrating an existing AMP setup, you still need a new create-scraper call with a cloudWatchConfiguration destination โ€” updating an existing scraper's destination isn't supported.
  • Using static_configs for ECS. Task IPs change on every deploy; a static target list goes stale within a day. Use Cloud Map and dns_sd_configs for anything running on ECS.
  • Skipping relabeling. Without compute_platform or similar tags, EC2 and ECS metrics land in CloudWatch with identical metric names and no way to filter one source from another in a shared dashboard.

Troubleshooting

If a scraper shows as active but no metrics appear in CloudWatch:

  1. Confirm the security group actually allows the collector to reach the exporter port โ€” check inbound rules on the target's security group, not just the collector's.
  2. If subnets are private, confirm the interface VPC endpoint for com.amazonaws.{region}.monitoring exists and its security group allows inbound HTTPS from the collector's security group.
  3. Check that the target's /metrics endpoint actually returns Prometheus exposition format โ€” curl it directly from an instance in the same VPC.
  4. For ECS, confirm the Cloud Map service actually has healthy task registrations โ€” dns_sd_configs returns nothing if Cloud Map's DNS records are empty.

FAQ

Does this replace Amazon Managed Service for Prometheus? No โ€” it's a new destination for the same underlying scraper. If you want long-term Prometheus-native storage, alerting via Alertmanager, or a Grafana-first workflow, AMP workspaces still make sense. If you just want Prometheus metrics inside CloudWatch alongside everything else, the managed collector with a CloudWatch destination is now the simpler path.

Do I need to run anything on my EC2 instances or ECS tasks besides the exporter? No. Node Exporter (or your app's own /metrics endpoint) is all you need on the target side โ€” the collector itself runs entirely on AWS-managed infrastructure outside your instances or tasks.

What's the pricing model? Collectors are billed hourly, plus standard CloudWatch OpenTelemetry metric ingestion pricing on top. There's no separate line item for the scrape compute itself beyond the hourly collector charge.

Which regions support this? All AWS Regions where the CloudWatch OTLP endpoint is available, except Asia Pacific (New Zealand), as of the July 2026 launch.

Can one scraper cover both EC2 and ECS targets? Yes, technically โ€” a single scrape config can have multiple scrape_configs job entries mixing static_configs and dns_sd_configs. In practice, splitting them into separate scrapers per source type makes relabeling, troubleshooting, and cost attribution simpler.

Key takeaways

BeforeNow
Self-managed ADOT Collector on EC2/FargateFully managed collector, no compute to own
Separate AMP workspace required for Prometheus dataOptional โ€” CloudWatch dataset destination works standalone
Manual scaling as targets growCloudWatch provisions and scales automatically
Prometheus metrics siloed from AWS-native metricsPromQL queries run alongside CloudWatch's own metrics

Further Reading