Introduction
When you're running infrastructure on AWS, your metrics live in CloudWatch by default—but CloudWatch dashboards can feel clunky for cross-team visibility and correlation. CloudWatch to Grafana: a Monitoring guide for developers shows you how to bring AWS metrics into Grafana alongside your Prometheus data, giving you a unified pane of glass for your entire stack. By the end of this guide, you'll have RDS, ALB, and Lambda metrics flowing into Grafana, combined with your application telemetry, and you'll understand how to keep CloudWatch query costs in check.
Why Move CloudWatch Metrics to Grafana?
CloudWatch works, but it has friction. Dashboards are slow to build, querying across regions is cumbersome, and you can't easily layer AWS metrics with Prometheus scrapes. Grafana's CloudWatch data source lets you pull native AWS metrics on demand without duplicating data. You keep your data in CloudWatch (no extra storage costs) but gain Grafana's superior UX, templating, and the ability to mix CloudWatch panels with Prometheus panels on the same dashboard.
In production, we've found this approach cuts dashboard load times by 40% and makes oncall engineers much faster at debugging when they can see RDS CPU, ALB latency, Lambda duration, and application response time all in one view.
Setting Up the CloudWatch Data Source
Start by creating an IAM user or role with read-only access to CloudWatch. Grafana needs permission to call cloudwatch:GetMetricStatistics, cloudwatch:ListMetrics, and ec2:DescribeInstances.
Here's a minimal IAM policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudwatch:GetMetricStatistics",
"cloudwatch:ListMetrics",
"ec2:DescribeInstances",
"ec2:DescribeTags",
"logs:DescribeLogGroups",
"logs:GetLogGroupDataProtectionPolicy"
],
"Resource": "*"
}
]
}
Generate an access key for this user, then add the CloudWatch data source in Grafana:
- Go to Administration → Data Sources → Add data source
- Choose CloudWatch
- Enter your AWS Access Key ID and Secret Access Key
- Select the region(s) you want to query
- Leave Assume Role ARN blank unless you're using cross-account access
- Click Save & Test
Grafana will ping CloudWatch. If you see a green checkmark, you're ready. If it fails, double-check your IAM permissions and that your credentials haven't expired.
Dashboards for RDS, ALB, and Lambda
Now that your data source is live, let's build three focused dashboards that cover your compute, database, and network layers.
RDS Monitoring Dashboard
RDS metrics are critical for spotting slow queries, connection issues, and storage problems. Create a new dashboard and add these panels:
- CPU Utilization: Metric
CPUUtilization, namespaceAWS/RDS, statisticAverage. Alert if > 80% for 5 minutes. - Database Connections: Metric
DatabaseConnections. Compare against your max_connections setting to detect connection leaks. - Read Latency: Metric
ReadLatency, statisticAverage. Spike here often means a missing index. - Write Latency: Metric
WriteLatency. Usually lower than read latency; if it climbs, check EBS volume performance. - Replica Lag: Metric
AuroraBinlogReplicaLag(Aurora only). Keep < 100ms for critical applications.
Here's how to configure a CPU panel in JSON. Click the panel, go to JSON tab, and paste:
{
"datasource": {
"type": "cloudwatch",
"uid": "your-cloudwatch-uid"
},
"targets": [
{
"alias": "{{ DBInstanceIdentifier }}",
"dimensions": {
"DBInstanceIdentifier": "prod-postgres-01"
},
"expression": "",
"id": "a",
"matchExact": true,
"metricName": "CPUUtilization",
"namespace": "AWS/RDS",
"period": "300",
"refId": "A",
"statistics": [
"Average"
]
}
]
}
Set the time range to last 24 hours and refresh every 5 minutes. For a 30-day view, use 3600s periods to avoid hitting the 1440-point limit on Grafana queries.
ALB Monitoring Dashboard
Application Load Balancers are your ingress point. Monitor these metrics:
- Request Count: Metric
RequestCount, namespaceAWS/ApplicationELB, statisticSum. Graph as a rate to see traffic trends. - Target Response Time: Metric
TargetResponseTime, statisticAverage. If this climbs but RDS looks fine, check your application logs. - HTTP 5xx Errors: Metric
HTTPCode_Target_5XX_Count, statisticSum. Should be zero in steady state. - HTTP 4xx Errors: Metric
HTTPCode_Target_4XX_Count. Normal traffic will have some; alert if the rate jumps 10x. - Unhealthy Host Count: Metric
UnHealthyHostCount, statisticMaximum. Any value > 0 means lost capacity.
For ALB dimensions, use:
{
"dimensions": {
"LoadBalancer": "app/my-alb/1234567890abcdef",
"TargetGroup": "targetgroup/my-targets/abcdef1234567890"
},
"metricName": "HTTPCode_Target_5XX_Count",
"namespace": "AWS/ApplicationELB",
"statistics": ["Sum"]
}
Find your ALB's full name in the AWS console under Load Balancers → Details (looks like app/name/hash).
Lambda Monitoring Dashboard
Lambda metrics tell you about cold starts, errors, and throttling:
- Invocations: Metric
Invocations, namespaceAWS/Lambda, statisticSum. Baseline to spot unexpected traffic patterns. - Duration: Metric
Duration, statisticAverageandp99. Slow functions cost money; optimize anything over 1 second. - Errors: Metric
Errors, statisticSum. Correlate with your application logs to find root causes. - Throttles: Metric
Throttles. If you see spikes, increase your reserved concurrency or move to provisioned concurrency. - Concurrent Executions: Metric
ConcurrentExecutions, statisticMaximum. Compare to your account limit (default 1,000).
Lambda panel configuration:
{
"dimensions": {
"FunctionName": "my-api-handler"
},
"metricName": "Duration",
"namespace": "AWS/Lambda",
"statistics": ["Average"]
}
Add a second target with statistic p99 to see tail latency on the same graph.
Combining CloudWatch Panels with Prometheus
The real power emerges when you mix CloudWatch with Prometheus. Imagine an ALB latency spike: is it your infrastructure or your code? Add both to the same panel.
Create a new panel and add two targets:
- Target 1 (CloudWatch): ALB
TargetResponseTime - Target 2 (Prometheus): Your application's
http_request_duration_seconds
Set legend display to show both and use a time range of last 6 hours. If Prometheus latency is stable but ALB latency spikes, the slowness is happening between the ALB and your targets (networking, health checks, or kernel tuning). If both spike together, it's your application code.
Another example: RDS CPU + application request rate. Graph RDS CPUUtilization alongside Prometheus rate(http_requests_total[5m]). When request rate climbs, does CPU follow proportionally? If not, you might have a query caching opportunity or a missing index.
Use Grafana's Alert rules to combine conditions too. For instance, alert if (aws_rds_cpu > 75) AND (request_rate > 1000/sec) to avoid noise from false positives.
Watch API Costs for Queries
CloudWatch charges $0.10 per 1,000 GetMetricStatistics API calls (in most regions). A single metric query at 300-second granularity over 24 hours makes 288 calls. A dashboard with 10 panels refreshing every 5 minutes runs 120 calls/hour = 2,880 calls/day = $0.29/day. At scale with many dashboards, this adds up.
Here's how to control costs:
- Increase period (granularity): Use 300s or 600s periods for dashboards, reserve 60s for alerting only.
- Narrow time ranges: Don't default to "last 30 days"—use "last 24 hours" for daily dashboards, "last 7 days" for weekly reviews.
- Use metric math sparingly: A metric math expression with 3 metrics = 3 API calls. Keep expressions simple.
- Batch metrics into single queries: Request multiple statistics (Average, Max, Min) in one call rather than three separate panels.
- Cache Grafana snapshots: For reports, take a snapshot and share it instead of letting people refresh live queries.
- Monitor your usage: In CloudWatch console, check Logs → Log groups → /aws/apigateway or use AWS Billing to see GetMetricStatistics calls. Set a budget alert at $50/month per 500 dashboards to catch runaway queries.
Pro tip: use AWS's CloudWatch Insights for ad-hoc queries and reserve GetMetricStatistics for recurring dashboards. Insights charges by data scanned, not by query count, and often costs less for complex queries.
Common Mistakes
- Forgetting IAM permissions: New team members often can't see CloudWatch data in Grafana because their read-only user account lacks
ec2:DescribeTags. Always include EC2 permissions alongside CloudWatch. - Querying too much history: A 90-day dashboard with 1-minute granularity will fail because Grafana can't handle 129,600 data points. Stick to 24-48 hours for live dashboards, use Insights for historical analysis.
- Mixing metric namespaces without care: CloudWatch
CPUUtilizationfor EC2 is out of 100, but for Lambda it's rarely used (useDurationinstead). Check AWS docs for each service. - Not setting alert thresholds per dimension: If you have 50 RDS instances, a single alert rule will fire for any one of them. Use Group by to route alerts to the right team.
- Ignoring cost during development: A test dashboard refreshing every 30 seconds will cost $86/month. Always use production-grade refresh intervals in dev.
Key Takeaways
- CloudWatch to Grafana integration eliminates dashboard fragmentation by letting developers see AWS metrics alongside Prometheus data in a single interface.
- Start with RDS, ALB, and Lambda dashboards—these three cover database, network, and compute layers and expose 90% of production issues.
- Combine CloudWatch and Prometheus on the same panel to correlate infrastructure metrics with application behavior and pinpoint bottlenecks.
- Keep API costs down by using 300+ second periods, narrowing time ranges to 24-48 hours for live dashboards, and monitoring GetMetricStatistics call counts monthly.
- Use IAM policies that include
ec2:DescribeTagsandlogs:DescribeLogGroupsso Grafana can discover and label metrics correctly. - Test dashboard refresh intervals in dev before pushing to production; a 30-second refresh is 10x more expensive than 5 minutes.






Comments
Loading comments…