ObservabilityXXVII · Dashboard Anti-PatternsDashboardAntiPatterns
Rainbow Color Palettes
What you'll learn
- Explain why a rainbow palette reduces operator discrimination between healthy and unhealthy values
- Recognise the accessibility failure of a rainbow palette under colour-blind viewing
- Apply a threshold-driven palette in Grafana 11.x with the right color mode and steps
- Choose between green/red and single-hue palettes depending on the metric direction
Prerequisites
Verified against Prometheus 2.55.x · Alertmanager 0.28.x · node_exporter 1.8.x · blackbox_exporter 0.26.x · Grafana 11.x · Loki 3.x · Tempo current · OpenTelemetry Collector 0.110.x · Grafana Alloy current · Docker Engine 28.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-13
The “Production Heatmap” panel on the SRE wall shows a matrix of service cells, each cell coloured on a continuous spectrum from violet to blue to green to yellow to orange to red. The violet end is “low value”; the red end is “high value”. The threshold between healthy and unhealthy is somewhere in the orange. The wall has been showing this heatmap for a year.
At 02:47 a service hits its SLO breach. The cell that represents it is bright orange. From three metres away it is indistinguishable from the eleven other orange cells that are just below their thresholds. The on-call engineer walks over, looks at the wall, and decides to check Slack first. They check the dashboard on their laptop, where the cell is unambiguously red. The wall failed the discrimination test the heatmap was supposed to pass.
A rainbow palette is any continuous colour scale that traverses the full hue range (red-orange-yellow-green-blue or similar) without anchoring to a threshold. It looks striking on first viewing; it fails the second viewing; it fails the third viewing more.
What it is
A rainbow palette in production is one of two things:
- A continuous gradient over the full hue range. A
heatmap or geomap where the colour at value
vis interpolated across hues, with no anchor at the threshold. The classical example is a “spectrum” or “rainbow” gradient on a Prometheus heatmap. - A categorical palette that maps each series to a different hue. A timeseries panel where every series is a different bright colour, regardless of value. There is no “red for trouble”; every colour is a different service.
In both shapes the palette carries no operational information. The colour is a label, not a signal. The operator has to read each cell to determine whether it is good or bad, which defeats the point of using colour at all.
The contrast is the threshold-driven palette: green for healthy, yellow for warning, red for breach. The colour changes only at thresholds. The operator does not read the value; the operator reads the colour.
Why a sysadmin cares
Three concrete production costs come from a rainbow palette:
- Reduced signal-to-noise. When every cell on a wall is a different colour, the operator’s eye cannot pick out the one that is exceptional. The wall becomes wallpaper (see lesson 01).
- Accessibility failure. Approximately 8% of men and 0.5% of women have some form of colour vision deficiency. The most common form (deuteranopia) collapses red and green into a single perceptual channel. A panel that distinguishes “good” from “bad” by red/green is invisible to those operators; a rainbow panel that uses red/green/orange interchangeably is worse than invisible, it is indistinguishable.
- Misleading under stress. Under incident pressure the operator’s perceptual discrimination narrows. They see colours but not their difference. A palette that requires three colours to be discriminated against each other is worse than a palette that requires one colour to be discriminated against the rest.
How it works
The right palette depends on whether the metric is “higher is worse”, “lower is worse”, or “near a target”:
Metric direction
|
+----+----+----+
| | |
Higher Target Lower
is bad band is bad
| | |
v v v
Green- Green- Green-
yellow- yellow- yellow-
red red red
(high (around (low
end = target = end =
red) green) red)
The same threshold-driven scheme handles all three: two thresholds, three colour zones, with the red zone at the end that is bad. The operator only has to learn “red is the direction that hurts”.
For a single-hue gradient (useful for heatmaps where the threshold is a single value and the direction is unambiguous), the rule is: light end = baseline, dark end = breach. The operator learns “the darker, the worse” and the palette preserves the discrimination without needing the eye to read each cell’s number.
Under the hood
Grafana 11.x has two colour systems:
- Threshold-based colour (
fieldConfig.defaults.thresholds). A list ofstepswith avalueandcolor. The colour is chosen by the highest step whosevalueis less than or equal to the data value. This is the right choice for stat panels and timeseries panels where the value has a clear threshold. - Continuous colour scheme (
fieldConfig.defaults.color). A scheme name (green-yellow-red,red-yellow-green,spectrum,rainbow, etc.) interpolated across the data range. This is the right choice for heatmaps and geomaps where the colour is part of the data, not a label.
The mistake is using scheme spectrum on a stat panel. A stat
panel has one value. The “spectrum” scheme assigns a different
colour to each value, which means a stat panel showing 42
versus 41 versus 40 versus 39 is a different colour
despite all four being “healthy”. That is rainbow-palette
behaviour on a panel type that should be threshold-driven.
The mistake is also using scheme green-yellow-red on a
heatmap. A heatmap has many cells; the colour should be
anchored to the threshold (the breach line) rather than to the
data range. The default Grafana heatmap mode is
mode: "fixed" with explicit min, max and step values,
which is the correct shape.
How to configure it
Stat panel with threshold-driven colour
{
"type": "stat",
"fieldConfig": {
"defaults": {
"unit": "percent",
"decimals": 2,
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null },
{ "color": "yellow", "value": 50 },
{ "color": "red", "value": 80 }
]
}
}
},
"options": {
"colorMode": "background",
"graphMode": "area",
"textMode": "auto"
}
}
Three colour zones, three thresholds, no rainbow. The panel is green under 50%, yellow between 50% and 80%, red at or above 80%.
Timeseries panel with single colour for the breach zone
{
"type": "timeseries",
"fieldConfig": {
"defaults": {
"unit": "s",
"custom": {
"drawStyle": "line",
"lineWidth": 2,
"fillOpacity": 5,
"thresholdStyle": {
"mode": "line",
"lineColor": "red",
"lineWidth": 2
}
},
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "red", "value": 1 }
]
}
}
}
}
The threshold line is drawn in red. The line of the metric crosses it at the SLO. The operator reads the crossing, not the colour of the metric line itself.
Heatmap with single-hue anchored to the breach threshold
{
"type": "heatmap",
"fieldConfig": {
"defaults": {
"custom": {
"hideFrom": { "tooltip": false, "viz": false, "legend": false },
"scale": {
"type": "linear",
"min": 0,
"max": 500
},
"color": {
"mode": "scheme",
"scheme": "Reds",
"exponent": 0.5,
"min": 0,
"max": 500,
"steps": 10
}
}
}
}
}
The scheme is Reds (a single-hue gradient, light to dark).
The minimum and maximum are explicit. The threshold of
300 is drawn as a separate threshold line on the heatmap.
A cell darker than the threshold line is the breach.
How to validate it
Walk the team’s dashboards and check the colour configuration of every panel. The CI check enforces three rules:
# READ-ONLY. Walk every dashboard in the team's folder and
# extract panels whose color scheme is "rainbow" or "spectrum".
curl -sS -H "Authorization: Bearer ${GRAFANA_TOKEN}" \
"${GRAFANA_URL}/api/search?folderIds=${FOLDER_ID}&type=dash-db" \
| jq -r '.[].uid' \
| while read uid; do
curl -sS -H "Authorization: Bearer ${GRAFANA_TOKEN}" \
"${GRAFANA_URL}/api/dashboards/uid/${uid}" \
| jq -r --arg uid "$uid" '
.dashboard.panels[]?
| select(.fieldConfig.defaults.color.scheme == "spectrum"
or .fieldConfig.defaults.color.scheme == "rainbow")
| "\($uid)\t\(.title)\t\(.type)\t\(.fieldConfig.defaults.color.scheme)"
'
done
Expected output (illustrative):
checkout-overview Heatmap of latency heatmap spectrum
payments-detail Error rate per endpoint heatmap rainbow
For each match, either justify the choice (rare) or change the
scheme to one of: Reds, Greens, Blues, green-yellow-red,
or red-yellow-green. Document the justification in the panel
description if the rainbow is intentional (for example, a
geomap of geolocated outages where the colour is genuinely a
data dimension).
For accessibility, run the dashboard through a colour-blindness simulator. Most browsers support this via developer tools, and several open-source projects simulate the three common forms (deuteranopia, protanopia, tritanopia) without additional software. The dashboard must remain readable in all three simulated views.
How it can fail
Six specific failure shapes attached to rainbow palettes:
- The continuous spectrum on a stat panel. A panel showing
0is one colour,1is another,2is another. Three distinct colours for three adjacent values. The operator cannot tell whether the change is meaningful. - The categorical palette with no threshold anchor. A timeseries with eight series, each a different bright colour. The breach is a series turning red, but no series is red; the breach is a different shape or annotation the operator has to read.
- The red/green-only palette without yellow. A panel that flips between green and red. The “yellow” zone is missing, so the operator cannot distinguish “warning” from “breach” and pages too early or too late.
- The colour-blind-unreadable palette. A panel that distinguishes “good” from “bad” by red versus green, with no shape, value or threshold line. Approximately one operator in twelve cannot see the difference.
- The dark-mode mismatch. A panel configured with the default light-mode palette, deployed against a dark theme. The “green” and “red” colours render as muted greys; the panel is unreadable.
- The palette that changed under upgrade. Grafana 11.x
changed the default threshold colour order to
green-yellow-red. A panel written against Grafana 9.x may have inheritedred-yellow-greenand looks reversed. The operator reads “warning” as “healthy”.
How to troubleshoot it
When a rainbow palette has caused an incident or a missed signal:
- Identify the panel. Open the dashboard and find the panel that was the signal. Note its UID and the colour scheme in its field options.
- Identify the threshold. Find the SLO for the metric. The threshold value is the SLO breach point.
- Replace the scheme. Set the colour scheme to
green-yellow-redfor higher-is-worse,red-yellow-greenfor lower-is-worse. For heatmaps and geomaps, pick a single-hue scheme and pin the min/max to fixed values. - Set the threshold explicitly. Set the threshold to the SLO breach value. Make it visible in the visualisation (line, band, marker).
- Validate in three colour-blind modes. Simulate deuteranopia, protanopia and tritanopia. The panel must remain readable in all three.
- Document the palette choice in the dashboard description. “All panels use the green-yellow-red threshold palette. Red is breach; yellow is warning; green is healthy.”
Security implications
A rainbow palette is mostly a usability and accessibility issue, not a security issue. The exception is the panel that intentionally uses colour to flag sensitive data: a panel that turns red when a personally identifiable information (PII) detector fires. If the palette is hard to read, the operator misses the alert. The discipline is the same: threshold-driven, colour-blind-readable, with the alert duplicated in a Slack notification that does not depend on the dashboard palette.
Performance implications
The palette itself is rendered client-side in the browser and has no measurable server cost. The cost of a rainbow palette is human, not machine: the seconds the operator spends discriminating between adjacent colours, and the incidents that are missed because the discrimination failed.
The exception is the heatmap palette. A continuous scheme
interpolated over a large cell matrix (O(10^4) cells) can
make the GPU compositing path slow. Single-hue schemes with
fixed min and max are cheaper to render because the colour
lookup is a closed-form function rather than an interpolation
across the full data range.
Production guidance
- Default the colour scheme to
green-yellow-redfor higher-is-worse metrics andred-yellow-greenfor lower-is-worse metrics. - For heatmaps, use a single-hue scheme (
Reds,Greens,Blues) with explicitminandmax. - For categorical series in a timeseries, use the Grafana 11.x default palette and rely on line shape, not colour, to mark the threshold.
- Lint the dashboard JSON in CI for any panel whose colour
scheme is
spectrumorrainbow. Require a comment in the panel description justifying the choice if the lint fires.
Verification
You should now be able to answer:
- Why does a rainbow palette reduce operator discrimination under incident conditions?
- Which two Grafana 11.x field options control threshold-based colour versus continuous colour?
- What is the right single-hue scheme for a heatmap of latency, and where do the threshold values come from?
- What three colour-blindness simulations must a dashboard panel pass before it ships?
- What is the upgrade-time failure mode of a palette that depended on the Grafana default order?
Quiz
Knowledge check · 8 questions
Q1. What is the operational purpose of colour on a dashboard panel?
Q2. Which of these are real failure shapes of rainbow palettes?
Q3. A rainbow palette is colour-blind safe because it uses many hues.
Q4. Which Grafana 11.x field option controls whether a panel uses threshold-based colour or continuous colour?
Q5. Name one single-hue colour scheme that is appropriate for a latency heatmap.
Q6. Which colour scheme is the right default for a higher-is-worse metric like error rate?
Q7. A dashboard upgraded from Grafana 9.x to 11.x can change the colour meaning of a panel if the panel relied on the default scheme order.
Q8. Which colour-blind simulations must a dashboard panel pass before it ships to production?
Passing score: 75%. Answers are checked in this browser.