Skip to content
AWS aws monitoring 5 min read

CloudWatch Dashboards

A CloudWatch dashboard is a single page where you pin charts (called widgets) so you can watch the health of many AWS services at once. Instead of clicking through the console for each metric, you build one screen that shows CPU, error rates, request counts, and logs side by side. CloudWatch is AWS’s monitoring service (it collects metrics and logs from almost every AWS service), and dashboards are how you turn that raw data into something a human can read at a glance. This page shows how to build one, add metric and log widgets, pull data from other Regions and accounts, and avoid the classic gotcha where a dashboard goes blank after your servers get replaced.

When to use a dashboard (and when not to)

Use a dashboard when you want an always-on view for a team — an on-call engineer glancing at one screen, or a wall-mounted display in an operations room. They are also great for a single service’s “golden signals” (latency, traffic, errors, saturation) grouped in one place.

Do not use a dashboard as your alerting system. A dashboard is passive — nobody is watching it at 3 a.m. For “tell me when something breaks,” you want a CloudWatch alarm that emails or pages you. A good setup uses both: alarms catch problems, and the dashboard helps you investigate once you’re paged.

Creating a dashboard with a metric widget

A metric widget plots one or more CloudWatch metrics over time as a line or stacked-area graph. Here is the smallest useful dashboard: average EC2 CPU for one instance.

Console steps:

  1. Open the CloudWatch console and choose Dashboards in the left menu.
  2. Click Create dashboard, name it prod-overview, and click Create dashboard again.
  3. In the widget picker choose Line, then Metrics.
  4. Browse to EC2 > Per-Instance Metrics, find CPUUtilization for your instance, and tick its checkbox.
  5. On the Graphed metrics tab set the statistic to Average and the period to 5 minutes.
  6. Click Create widget, then Save at the top of the dashboard.

CLI equivalent. A dashboard is just a JSON document describing its widgets. You define the JSON and push it with put-dashboard.

aws cloudwatch put-dashboard \
  --dashboard-name prod-overview \
  --dashboard-body '{
    "widgets": [
      {
        "type": "metric",
        "x": 0, "y": 0, "width": 12, "height": 6,
        "properties": {
          "title": "EC2 CPU (avg)",
          "region": "us-east-1",
          "stat": "Average",
          "period": 300,
          "metrics": [
            [ "AWS/EC2", "CPUUtilization", "InstanceId", "i-0a1b2c3d4e5f" ]
          ]
        }
      }
    ]
  }'

Output:

{
    "DashboardValidationMessages": []
}

An empty DashboardValidationMessages list means the JSON was accepted. If you mistype a property, AWS returns a warning here instead of failing silently.

Adding a log widget

A log widget runs a CloudWatch Logs Insights query and shows the result as a table or chart, so you can see actual log lines next to your metrics. This is useful for things like “show me the last 20 errors” right beside the error-rate graph.

In the console, add a widget, choose Logs table, pick your log group, and enter a Logs Insights query such as:

fields @timestamp, @message
| filter @message like /ERROR/
| sort @timestamp desc
| limit 20

In JSON the same widget uses "type": "log" and a query property:

{
  "type": "log",
  "x": 0, "y": 6, "width": 24, "height": 6,
  "properties": {
    "region": "us-east-1",
    "title": "Recent errors",
    "query": "SOURCE '/aws/lambda/checkout' | fields @timestamp, @message | filter @message like /ERROR/ | sort @timestamp desc | limit 20",
    "view": "table"
  }
}

Cross-Region and cross-account views

Each widget has its own region property, so one dashboard can show us-east-1 and eu-west-1 graphs together — just set the Region per widget.

For cross-account views, enable CloudWatch cross-account observability: in the monitoring account, link your source accounts (Settings > Cross-account observability). Once linked, the widget JSON takes an accountId property, and the console shows an account dropdown on each graph. This lets a central operations account watch every team’s metrics from one dashboard without logging in and out.

Tip: Cross-account observability shares metrics, logs, and traces one way (source to monitoring account). Set it up with AWS Organizations so new accounts link automatically.

Metric math and Search expressions

Metric math lets you compute new values from existing metrics — for example, turning request and error counts into an error rate. Search expressions dynamically match metrics by a pattern instead of naming each one, which is the key to surviving infrastructure changes (see the gotcha below).

A widget that plots the error percentage across an Auto Scaling group, no matter which instances exist:

"metrics": [
  [ { "expression": "SEARCH('{AWS/ApplicationELB,LoadBalancer} MetricName=\"HTTPCode_Target_5XX_Count\"', 'Sum', 300)", "id": "e1", "visible": false } ],
  [ { "expression": "SEARCH('{AWS/ApplicationELB,LoadBalancer} MetricName=\"RequestCount\"', 'Sum', 300)", "id": "e2", "visible": false } ],
  [ { "expression": "100 * SUM(e1) / SUM(e2)", "id": "errorRate", "label": "5xx %" } ]
]

The blank-dashboard gotcha

The most common dashboard failure is stale instance IDs. If you pin a widget to i-0a1b2c3d4e5f and that instance is later terminated — which happens every time an Auto Scaling group (a service that automatically adds and removes EC2 instances based on load) replaces a server — the metric disappears and the graph goes blank. The dashboard does not auto-discover the new instances.

The fix is to stop naming instances individually:

  • Use a SEARCH expression so the widget matches any instance currently reporting the metric (shown above).
  • Or aggregate by a dimension you control, such as the Auto Scaling group name, instead of by instance ID.
  • Or generate the dashboard JSON from a tag query in code or EventBridge automation, so it rebuilds when resources change.

Cost gotcha: The first 3 dashboards (each with up to 50 metrics) are free. Beyond that, each dashboard costs about $3 per dashboard per month. Consolidate widgets onto fewer dashboards rather than creating one per microservice, and delete dashboards you no longer watch.

Best practices

  • Build dashboards with SEARCH expressions or group-level dimensions so they survive instance churn — never hard-code instance IDs you don’t manage by hand.
  • Keep alerting in alarms; use dashboards only for at-a-glance viewing and investigation.
  • Store dashboard JSON in source control (CloudFormation AWS::CloudWatch::Dashboard or Terraform aws_cloudwatch_dashboard) so changes are reviewed and reproducible.
  • Group widgets by service and put the most important “golden signals” at the top-left where eyes land first.
  • Use metric math to show rates and ratios (error %, success %) rather than raw counts that are hard to interpret.
  • Stay within the free 3-dashboard / 50-metric allotment where you can, and prune unused dashboards to avoid the per-dashboard monthly charge.
Last updated June 15, 2026
Was this helpful?