Dashboard

What goes in an AWS monitoring dashboard in Metabase?

An AWS monitoring dashboard puts the health of your EC2 fleet, load balancers, volumes, databases, and caches on one page — built from CloudWatch metric rollups exported to a database, where they can be joined to owners, releases, and spend. CloudWatch keeps the pager; this is the review and reporting layer on top.

For: platform engineers, SREs, and engineering leadership. Grain: one row per resource per hour or day — rollups, never raw datapoints. Refresh: hourly.

What does an AWS monitoring dashboard look like?

Here’s the layout this guide builds. Fleet-level counts and the two red numbers sit at the top; compute and traffic trends fill the middle; storage, database, and cache detail sits at the bottom with the failing-instances table you drill into when a status check goes red.

AWS monitoring dashboard in Metabase showing CPU by instance group, ALB traffic and latency, EBS throughput, RDS connections, and failing status checks.
An example AWS monitoring dashboard in Metabase, built from CloudWatch metric rollups. Figures are illustrative.

Which cards belong on an AWS monitoring dashboard?

The classic eight, modernized from per-instance graphs to per-group ones — at fleet size, groups are what you can actually act on.

  • CPU utilization by instance group, against a scaling threshold (line)
  • Request count from the load balancer (area)
  • Latency — ALB target response time p50 and p95 (line)
  • Disk read/write throughput across the fleet’s volumes (line)
  • Node status — instances failing status checks (number + table)
  • Database connections against max_connections, per RDS instance (line)
  • Disk queue depth on the busiest volumes (line)
  • Cache hits vs. misses from ElastiCache (line)

What data does the dashboard need?

  • cloudwatch_metric_rollups — one row per resource, metric, and window: EC2 (CPUUtilization, StatusCheckFailed, disk and network IO), ALB (RequestCount, TargetResponseTime percentiles), EBS (VolumeQueueLength, throughput), RDS (DatabaseConnections), and ElastiCache (hits and misses).
  • instances — an inventory with instance group, environment, region, AZ, and owner tags, so charts group by something actionable.
  • alarms — current CloudWatch alarm states, for the “alarms in ALARM” card.
  • Optional daily cost per resource from the Cost and Usage Report, for spend-aware cards.

How do you build it?

  1. Export CloudWatch metrics on a schedule — Metric Streams via Firehose to S3, or a GetMetricData job — and land hourly rollups in a database Metabase can query (Athena over S3 works; see the Athena data source guide).
  2. Keep percentiles pre-aggregated at the source — p95 latency can’t be reconstructed from stored averages later.
  3. Build an instance inventory with group, environment, and owner tags, and join every metric through it — untagged fleets produce unactionable charts.
  4. Start with the status-check and CPU cards, then traffic and latency, then the storage and database detail.
  5. Add filters for instance group, region, environment, and date range, and keep alerting in CloudWatch — this page is for review, not paging.

Example card SQL

CPU, requests, latency, and status checks by instance group by day PostgreSQL
SELECT
i.instance_group,
date_trunc('day', m.window_start)                    AS day,
ROUND(AVG(m.value) FILTER (
  WHERE m.metric = 'CPUUtilization'), 1)             AS cpu_avg_pct,
ROUND(MAX(m.value) FILTER (
  WHERE m.metric = 'CPUUtilization'), 1)             AS cpu_peak_pct,
ROUND(SUM(m.value) FILTER (
  WHERE m.metric = 'RequestCount') / 1e6, 2)         AS requests_m,
MAX(m.value) FILTER (
  WHERE m.metric = 'TargetResponseTime.p95') * 1000  AS latency_p95_ms,
MAX(m.value) FILTER (
  WHERE m.metric = 'StatusCheckFailed')              AS status_checks_failed
FROM cloudwatch_metric_rollups m
JOIN instances i ON i.instance_id = m.instance_id
WHERE m.window_start >= now() - interval '14 days'
GROUP BY 1, 2
ORDER BY 1, 2;

Metrics

Integrations

Dashboards

FAQ

What is an AWS monitoring dashboard?
An AWS monitoring dashboard tracks the health of your AWS estate — EC2 CPU and status checks, load-balancer traffic and latency, EBS throughput and queue depth, RDS connections, and cache behaviour — in one place, at a review grain. CloudWatch owns the raw metrics and the 3 a.m. alarm; this dashboard works on hourly or daily rollups exported to a database, where you can join them to deploys, tickets, and billing data, and share them with people who do not have AWS console access.
How do I get CloudWatch metrics into a database?
Three well-worn routes. CloudWatch Metric Streams pushes near-real-time metrics through Kinesis Data Firehose to S3, where Athena or a warehouse load reads them — the most AWS-native path. A scheduled job calling GetMetricData writes exactly the rollups you want and nothing more. Or, if an observability platform already collects your AWS metrics, export its rollups instead — Prometheus remote-write storage, Datadog metric queries, or Grafana's data sources all work. Whichever route, land one row per resource, metric, and window.
Does this replace CloudWatch dashboards and alarms?
No. CloudWatch keeps sub-minute resolution, per-resource alarms, and paging — nothing in Metabase should try to compete with that. What CloudWatch is weak at is joining infrastructure metrics to everything else: which team owns the instance, what the month's spend looks like next to utilization, whether the latency regression lines up with a release. That cross-source view, shared with people who will never open the AWS console, is the job of this dashboard.
Why is average CPU utilization misleading?
Two reasons. Averaging across a fleet hides the one saturated group — 46% fleet CPU with api-prod pinned at 76% is an action item the average conceals, which is why the example groups by auto-scaling group. And on burstable (T-family) instances, CPU percent without CPU credit balance is incomplete: a t3 at a modest 40% that has exhausted its credits is being throttled. Chart per-group percentiles, and add CPUCreditBalance for burstable fleets.
What does EBS queue depth tell me?
How many IO requests are waiting versus being served — the earliest clean signal that a workload is IO-bound. A sustained queue depth near 1 per provisioned 1,000 IOPS is healthy; sustained values well above that mean the volume is undersized for the workload, and latency will follow. A nightly spike that lines up with a batch job, like the 02:00 pattern in the example, is usually cheaper to fix by rescheduling or splitting the job than by provisioning more IOPS.
How should I monitor RDS connections?
Chart the daily peak of DatabaseConnections against the instance's max_connections, not the average — pool exhaustion happens at peak. Sustained peaks above roughly 80% of the limit mean it's time for a pooler (RDS Proxy or PgBouncer) or a limit review. For Postgres engines, pair this card with a PostgreSQL monitoring dashboard built from pg_stat views — CloudWatch sees the instance, the database's own statistics explain it.
Can I join monitoring data to AWS cost?
Yes, and it's the strongest reason to have these metrics in a database. Land the Cost and Usage Report alongside the metric rollups, join on resource ID and tags, and utilization-versus-spend questions become one query: which instance groups run under 25% CPU on peak days, what the failing-status-check instances cost a month, how spend per request trends. See AWS billing + Metabase and the infrastructure cost dashboard for that side.