Build a Grafana dashboard that operators can trust. Set useful queries, panels, variables, units, thresholds, refresh timing, alerts, and layout.
The useful version answers a narrow question such as, “Is the checkout service healthy right now, and what changed?” The useless version displays every metric the team happens to collect. Grafana makes it easy to add panels. That convenience is exactly why restraint matters.
This guide builds a practical Grafana dashboard from the decision outward. It covers queries, variables, panel choice, units, thresholds, time ranges, alerting, and the layout mistakes that make an operations screen hard to trust.
Start a Grafana dashboard with one operating question
Do not start by connecting Prometheus, PostgreSQL, Loki, or another source. Start by writing the sentence the dashboard must answer. A service owner may need to know whether customers can complete checkout. A database administrator may need to know whether query latency is climbing because connections are exhausted. Those are different dashboards even when they read from the same monitoring stack.
A useful brief has four parts:
- Audience: the person who will keep the screen open.
- Decision: what that person does after reading it.
- Scope: the service, region, customer, or environment being watched.
- Time window: whether the decision uses five minutes, one day, or one quarter.
For example: “This dashboard helps the on-call engineer decide whether to investigate or roll back the checkout service by watching request rate, error rate, latency, and saturation over the last two hours.” That sentence gives every panel a reason to exist. If a chart does not support the decision, remove it or move it to a drill-down dashboard.
Choose metrics before writing queries
Most operational Grafana dashboards need a small set of signals rather than dozens of raw counters. A reliable starting point is traffic, errors, latency, and saturation. The labels change by system, but the logic does not.
| Signal | Question it answers | Typical measure |
|---|---|---|
| Traffic | How much work is arriving? | Requests per second, jobs per minute, active users |
| Errors | How much work is failing? | Error percentage, failed jobs, HTTP 5xx rate |
| Latency | How long does successful work take? | Median, p95, and p99 duration |
| Saturation | Which resource is running out? | CPU, memory, queue depth, connection pool use |
Put a target or threshold beside each signal. “p95 latency is 620 ms” is a measurement. “p95 latency is 620 ms against a 500 ms objective” is an operating decision. This is the same distinction explained in the KPI versus metric guide: context turns a number into something a person can act on.
Write queries that preserve meaning
The query layer should return the smallest useful result. Do not send millions of raw rows to the browser and ask a panel to summarize them. Aggregate at the cadence the decision needs, keep label sets under control, and name series so another person can understand them during an incident.
A Prometheus request-rate query might look like this:
sum by (service) ( rate(http_requests_total{service=~"$service"}[5m]) ) The five-minute rate smooths short counter jumps without hiding a sustained change. The service variable lets the same dashboard inspect one service or a controlled group. Be careful with labels such as user ID, request ID, or full URL. They create high-cardinality series, which increase storage and make queries expensive.
For a PostgreSQL source, let Grafana apply the dashboard time range rather than hard-coding dates:
SELECT $__timeGroupAlias(created_at, '5m'), COUNT(*) AS orders FROM orders WHERE $__timeFilter(created_at) GROUP BY 1 ORDER BY 1; The query returns one row per five-minute bucket. That is enough for a trend panel and far cheaper than returning every order. Use a materialized view or recording rule when the same expensive calculation appears across several dashboards.
Match each Grafana panel to the question
Grafana offers many visualizations, but an operational dashboard usually needs only a handful. The panel should match the comparison the reader must make.
- Stat: one current value against a target, such as error rate or open incidents.
- Time series: change over time, such as latency or request rate.
- Bar gauge: several current values measured on the same scale.
- Table: exact records, owners, or the worst offenders.
- Heatmap: a distribution that changes over time, such as request duration buckets.
- State timeline: discrete states such as healthy, degraded, and unavailable.
Do not use a gauge just because the screen is operational. A gauge spends a large amount of space showing one number and often hides the trend that explains it. A stat panel with a sparkline is usually easier to scan. The dashboard widgets guide covers the same trade-off across other dashboard tools.
Use variables for scope, not for building a maze
Dashboard variables are useful when operators need the same view for production and staging, several regions, or a small number of services. They are less useful when one dashboard attempts to cover every team and every system.
Keep the variable row short. Environment, region, and service are usually enough. Put the safest default first, normally production plus the most important region. Use an “All” option only when the combined query remains meaningful and affordable. A graph with 80 service lines does not become useful because the viewer can technically filter it.
Include variable values in panel titles. “Error rate” forces the reader to inspect the filter bar. “Checkout error rate, production” carries its own context into screenshots, incident notes, and exported reports.
Set units, decimals, thresholds, and null handling explicitly
A dashboard loses trust through small ambiguities. A value of 0.42 might mean 42 percent, 0.42 seconds, or 0.42 requests per second. Set the unit in every panel. Round to the precision the decision needs. Nobody investigating a service needs CPU use displayed as 63.8472 percent.
Thresholds should come from an objective or an operating limit, not from whatever creates an attractive color split. If the service-level objective allows one percent errors, make that boundary visible. Use a reference line on the time series and the same threshold in the stat panel. Consistent color should mean consistent status across the whole dashboard.
Decide how nulls behave. A null value can mean no traffic, a failed query, a missing scrape, or a genuine zero. Converting every null to zero can make a broken data pipeline look healthy. Label “no data” separately unless the domain guarantees that missing means zero.
Choose a time range and refresh interval the data can support
The default time range should match the event the dashboard detects. A two-hour view works for a service incident. A seven-day view works for daily batch performance. A quarterly view belongs on an executive screen, not an on-call console.
Refresh faster only when a faster decision is possible. A five-second refresh against a source that updates every minute creates load without adding information. For most service dashboards, 30 seconds or one minute is enough. High-frequency wallboards may need more, while a real-time dashboard should justify the infrastructure cost with a decision that truly cannot wait.
Show the selected time range and last refresh. During an incident, stale data is not a cosmetic problem. It changes the diagnosis.
Lay out the dashboard in the order people investigate
The top row should answer whether the system is healthy. Put four to six stat panels there: traffic, error rate, p95 latency, saturation, and perhaps deployment status. The next row explains movement with time series. The bottom row identifies where to look with tables, logs, or a regional breakdown.
This is the inverted-pyramid layout from the dashboard layout guide. Status comes first, explanation second, detail last. Keep related panels the same width and align their time axes. Use collapsible rows for secondary diagnostics, but do not hide the signals required for the first decision.
Panel titles should state what is being measured and, when useful, what changed. “Latency” is a label. “Checkout p95 latency against the 500 ms objective” is a complete instruction for reading the chart.
Treat alerts and dashboards as two views of the same rule
An alert tells someone to open the dashboard. The dashboard should immediately show the same metric, threshold, labels, and time window that triggered the alert. If an alert fires on a five-minute error-rate query but the dashboard defaults to a one-hour average, the responder starts by reconciling two different truths.
Link the alert notification to a dashboard with the relevant service, region, and time range already selected. Add a short annotation when deployments occur. A vertical deployment marker often explains a sudden change faster than another panel.
Common Grafana dashboard mistakes
- Too many panels. A screen with 40 panels delays the first decision and produces expensive queries.
- Unbounded variables. An “All” selection that expands to hundreds of series can slow the dashboard or the data source.
- Color without a rule. Five decorative colors make the one real alert color easier to miss.
- Average-only latency. The average hides the slow requests users remember. Show p95 or p99 beside the median.
- Nulls rendered as zero. Missing telemetry can look like perfect health.
- One dashboard for every audience. Operators, developers, and executives need different time ranges and different levels of detail.
- No source or owner. Put the owning team and data source in the dashboard description so the next person knows where to ask questions.
A reusable Grafana dashboard build checklist
- Write the audience, decision, scope, and time window in one sentence.
- Select four to six signals with targets or operating limits.
- Aggregate queries before the browser and control label cardinality.
- Use stat panels for current status and time series for movement.
- Limit variables to environment, region, service, or another decision-relevant scope.
- Set every unit, decimal rule, threshold, and null behavior explicitly.
- Match refresh frequency to source frequency and response speed.
- Arrange status first, explanation second, and detail last.
- Make alert queries and dashboard queries describe the same condition.
- Test the finished screen with the five-second question: is the system healthy, and where should I look next?
A good Grafana dashboard is not the one with the most panels. It is the one that shortens the path from a signal to the correct action. Build the operating question first, then let each query and panel earn its place.