Skip to main content
RunBook Academy

ObservabilityXXVII · Dashboard Anti-PatternsDashboardAntiPatterns

Rainbow Color Palettes

Foundation⏱ ~14 minbash

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

Not yet marked complete on this device.

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:

  1. A continuous gradient over the full hue range. A heatmap or geomap where the colour at value v is interpolated across hues, with no anchor at the threshold. The classical example is a “spectrum” or “rainbow” gradient on a Prometheus heatmap.
  2. 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:

  1. Threshold-based colour (fieldConfig.defaults.thresholds). A list of steps with a value and color. The colour is chosen by the highest step whose value is 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.
  2. 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:

  1. The continuous spectrum on a stat panel. A panel showing 0 is one colour, 1 is another, 2 is another. Three distinct colours for three adjacent values. The operator cannot tell whether the change is meaningful.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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 inherited red-yellow-green and looks reversed. The operator reads “warning” as “healthy”.

How to troubleshoot it

When a rainbow palette has caused an incident or a missed signal:

  1. 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.
  2. Identify the threshold. Find the SLO for the metric. The threshold value is the SLO breach point.
  3. Replace the scheme. Set the colour scheme to green-yellow-red for higher-is-worse, red-yellow-green for lower-is-worse. For heatmaps and geomaps, pick a single-hue scheme and pin the min/max to fixed values.
  4. Set the threshold explicitly. Set the threshold to the SLO breach value. Make it visible in the visualisation (line, band, marker).
  5. Validate in three colour-blind modes. Simulate deuteranopia, protanopia and tritanopia. The panel must remain readable in all three.
  6. 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-red for higher-is-worse metrics and red-yellow-green for lower-is-worse metrics.
  • For heatmaps, use a single-hue scheme (Reds, Greens, Blues) with explicit min and max.
  • 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 spectrum or rainbow. 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

  1. Q1. What is the operational purpose of colour on a dashboard panel?

  2. Q2. Which of these are real failure shapes of rainbow palettes?

  3. Q3. A rainbow palette is colour-blind safe because it uses many hues.

  4. Q4. Which Grafana 11.x field option controls whether a panel uses threshold-based colour or continuous colour?

  5. Q5. Name one single-hue colour scheme that is appropriate for a latency heatmap.

  6. Q6. Which colour scheme is the right default for a higher-is-worse metric like error rate?

  7. 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.

  8. Q8. Which colour-blind simulations must a dashboard panel pass before it ships to production?

Passing score: 75%. Answers are checked in this browser.