Reference

Visualizations

The built-in visualizations, the spec options each accepts, how each shapes query results, and the time-range parameters panels receive.

A panel's plugin.kind selects its visualization and plugin.spec configures it. Everr ships eleven visualizations. An unrecognized kind renders an "Unknown visualization" placeholder rather than breaking the dashboard.

They infer their structure from the columns your query returns — see Shaping data for each visualization below for the data shapes.

Options are validated: everr apply rejects a dashboard whose option values don't match the tables below, naming the file and the offending option. Unknown option keys are accepted and preserved. If an already-stored dashboard contains an invalid value, the panel still renders with the default and shows a warning icon in its header listing the ignored options.

TimeSeriesChart

Line chart over time. Supports drag-to-zoom (writes the new range to the URL). Data shape: see Time series.

plugin:
  kind: TimeSeriesChart
  spec:
    unit: ms
    showLegend: true
    lineWidth: 1.5
    curveType: monotone
    connectNulls: false
    stacked: false
OptionTypeDefaultNotes
unitstring""Suffix appended to axis/values.
showLegendbooleanfalseShow the series legend.
lineWidthnumber1.5Stroke width.
curveTypeenummonotoneOne of monotone, linear, natural, stepBefore, stepAfter.
connectNullsbooleanfalseBridge gaps instead of breaking the line.
stackedbooleanfalseStack series on top of each other as filled areas. A series with no sample at a timestamp contributes 0 there, and connectNulls has no effect.

BarChart

Bar chart over time or over categories. Data shape: see Bar.

plugin:
  kind: BarChart
  spec:
    unit: req
    showLegend: true
    stacking: stacked
    orientation: vertical
    showValues: false
OptionTypeDefaultNotes
unitstring""Suffix appended to axis/values.
showLegendbooleanfalseShow the series legend.
stackingenumnonenone draws series side by side; stacked piles them into one bar per x value; percent additionally normalizes each stack to 100% (the value axis becomes percentages, tooltips keep raw values).
orientationenumverticalvertical draws bars bottom-up; horizontal draws them left-to-right with categories on the y-axis (best for categorical data with long labels).
showValuesbooleanfalseDraw the value on each bar (on top/right of grouped bars, centered inside stacked segments).

Table

Renders query results as a table. Data shape: see Table.

plugin:
  kind: Table
  spec:
    stickyHeader: true
OptionTypeDefaultNotes
stickyHeaderbooleanfalseKeep the header visible while scrolling.

StatChart

Reduces a numeric column to a single big value via a calculation, with an optional sparkline and threshold coloring. Data shape: see Stat and gauge.

Values at or above one million abbreviate (12345671.23M); smaller values keep thousands grouping. The tile label is shown automatically with multiple tiles and hidden for a single tile unless showLabel is set.

plugin:
  kind: StatChart
  spec:
    calculation: last
    unit: ms
    decimals: 1
    sparkline: true
    thresholds:
      mode: percent
      max: 500
      defaultColor: "#22c55e"
      steps:
        - { value: 40, color: "#f59e0b" }
        - { value: 80, color: "#ef4444" }
OptionTypeDefaultNotes
calculationenumlastOne of last, first, mean, min, max, sum, count, range, diff.
unitstring""Suffix appended to the value.
decimalsnumberFixed fraction digits (0–10). Omitted: up to 2, trailing zeros dropped.
sparklinebooleanfalseDraw a sparkline (in time order, or row order without a time column).
colorModeenumvaluevalue tints the number; background fills the whole tile with the threshold color.
showLabelbooleanfalseShow the series label on a single-tile panel too.
noValuestringText shown for a query that produced no value.
thresholdsobjectColor the value (and sparkline) by thresholds.
thresholds.modeenumabsoluteabsolute compares the raw value; percent compares against thresholds.max.
thresholds.maxnumberseries maxReference for percent mode. Set it (e.g. an SLA ceiling) for percentages that don't shift with the time window.
thresholds.defaultColorstringColor when no step is crossed.
thresholds.steps[]{ value, color }Ascending thresholds; the highest crossed step's color wins.

GaugeChart

Reduces a numeric column to a single value — same calculations as StatChart — and renders it as a semicircular arc filled proportionally between min and max. Values outside the bounds clamp the arc to empty/full while the centered text still shows the real value. Data shape: see Stat and gauge.

Threshold steps color the filled arc and draw a tick mark on the gauge at each step's position, so the at-a-glance read is "how close to the next band am I".

plugin:
  kind: GaugeChart
  spec:
    calculation: last
    unit: "%"
    min: 0
    max: 100
    thresholds:
      defaultColor: "#22c55e"
      steps:
        - { value: 70, color: "#f59e0b" }
        - { value: 90, color: "#ef4444" }
OptionTypeDefaultNotes
calculationenumlastOne of last, first, mean, min, max, sum, count, range, diff.
unitstring""Suffix appended to the value.
decimalsnumberFixed fraction digits (0–10). Omitted: up to 2, trailing zeros dropped.
minnumber0Gauge axis lower bound.
maxnumber100Gauge axis upper bound. Set it to the metric's real ceiling — the arc reads as value ÷ range.
showLabelbooleanfalseShow the series label on a single-gauge panel too.
noValuestringText shown for a query that produced no value.
thresholdsobjectColor the arc and mark step positions on the gauge.
thresholds.modeenumabsoluteabsolute compares the raw value; percent compares against thresholds.max.
thresholds.maxnumbergauge maxReference for percent mode.
thresholds.defaultColorstringArc color when no step is crossed.
thresholds.steps[]{ value, color }Ascending thresholds; the highest crossed step's color wins. Each step also draws a tick on the gauge.

GeoMap

Plots geographic data on a world map, either as latitude/longitude markers (points) or by shading countries (choropleth). Data shape: see Geo map.

plugin:
  kind: GeoMap
  spec:
    mode: points
    latColumn: lat
    lonColumn: lon
    valueColumn: value
    unit: req
    showLegend: true
    colorScheme: blue
OptionTypeDefaultNotes
modeenumpointspoints plots lat/lon markers; choropleth shades countries.
latColumnstringlatLatitude column (points mode).
lonColumnstringlonLongitude column (points mode).
regionColumnstringregionISO-3166 alpha-2/alpha-3 column (choropleth mode).
valueColumnstringvalueSizes markers / shades regions.
labelColumnstringTooltip title; falls back to the region or coordinates.
aggregationenumsumChoropleth mode: how rows that map to the same region combine — sum, avg, min, max, or last. Use avg/max for non-additive metrics like latency percentiles or error rates; sum is only correct for additive metrics like request counts.
unitstring""Value formatting in the tooltip and legend.
showLegendbooleantruePoints: the value→marker-size mapping, plus per-query swatches when the panel has multiple queries. Choropleth: the color ramp.
colorSchemeenumblueOne of blue, green, orange, red — marker base / choropleth ramp.
projectionenumnaturalEarth1Map projection — naturalEarth1, mercator, or equalEarth.
scaleTypeenumlinearValue→color/size curve, applied to marker radius (points) and color intensity (choropleth). sqrt keeps marker area proportional to the value (perceptually honest proportional symbols); log spreads heavily skewed data (e.g. one country dominating traffic) so smaller values stay visible. For log, when the domain minimum is ≤ 0 the scale spans the top three decades below the maximum.
minRadiusnumber3Points mode: smallest marker radius in viewBox units (the map viewBox is 980×500).
maxRadiusnumber22Points mode: largest marker radius in viewBox units.
minnumberLower bound of the color/size domain. When unset: 0 in choropleth mode, so the ramp and legend read 0→max and no shaded region fades out entirely (the data minimum is used instead if values go negative); the data minimum in points mode.
maxnumberUpper bound of the color/size domain. Auto-derived from data when unset.

Treemap

Tiles whose areas are proportional to a numeric column — good for part-of-whole breakdowns like requests per route or storage per table. Data shape: see Treemap.

With groupColumn, tiles are colored by that column's value and the legend lists the groups (a label can appear once per group). Without it, a multi-query panel colors tiles by query, and a single ungrouped query cycles the palette per tile.

plugin:
  kind: Treemap
  spec:
    nameColumn: route
    valueColumn: requests
    groupColumn: service
    unit: req
    showValues: true
    showLegend: true
OptionTypeDefaultNotes
nameColumnstringnameTile label column.
valueColumnstringvalueTile size column — must be positive; rows at or below 0 are dropped.
groupColumnstringGroup tiles by this column: one color per group, legend lists the groups. Rows with a null group are dropped.
maxTilesnumberCap the tile count (≥ 2): the largest maxTiles - 1 tiles stay, the rest merge into a single muted "Other (n)" tile so long tails don't render as slivers. Unset renders every row.
unitstring""Value formatting in tiles and the tooltip.
showValuesbooleantrueRender the value inside tiles that are large enough to fit it.
showLegendbooleantrueGroup color legend — only shown when there are groups (from groupColumn or multiple queries).

StateTimeline

Horizontal lanes of colored segments showing discrete states over time — service health, deploy status, CI results. Supports drag-to-zoom (writes the new range to the URL). Data shape: see State timeline.

plugin:
  kind: StateTimeline
  spec:
    seriesColumn: service
    stateColumn: status
    mergeConsecutive: true
    showValues: true
    showLegend: true
    colors:
      ok: "#22c55e"
      error: "#ef4444"
OptionTypeDefaultNotes
seriesColumnstringLong-format input: one lane per distinct value of this column. Unset, every non-time column is its own lane.
stateColumnstringfirst remaining columnState column for long-format input. Ignored without seriesColumn.
mergeConsecutivebooleantrueMerge consecutive samples with the same state into one segment. false renders one box per sample — though if that's what you want, StatusHistory is usually the better fit.
showValuesbooleantrueRender the state text inside segments wide enough to fit it.
showLegendbooleantrueState color legend below the timeline.
colorsobject{}Fixed state → CSS color mapping (e.g. { ok: "#22c55e" }); unmapped states cycle the shared palette.
rowHeightnumber0.9Segment thickness as a fraction of the lane height (0.2–1).

StatusHistory

Horizontal lanes of discrete colored cells, one per sample — periodic health checks, cron-job runs, CI builds per interval, SLO probes. Supports drag-to-zoom (writes the new range to the URL). Data shape: see Status history.

StatusHistory vs StateTimeline: they share the same data shapes but answer different questions. StateTimeline treats samples as state transitions — a state holds until the next sample, segments stretch to fill time, and consecutive equal states merge into one band. Use it for things that are continuously in a state (service up/down, deploy phases, leader/follower) where "how long was it in state X, and when did it change?" is the question. StatusHistory treats samples as independent observations — each one is its own cell, nothing extends or merges, and a gap in sampling stays visibly empty instead of being painted over by the previous result. Use it for periodic checks and runs (health probes, cron jobs, per-bucket CI status) where "what did each check return — and did it run at all?" is the question.

plugin:
  kind: StatusHistory
  spec:
    seriesColumn: check
    stateColumn: result
    showValues: false
    showLegend: true
    colors:
      pass: "#22c55e"
      fail: "#ef4444"
OptionTypeDefaultNotes
seriesColumnstringLong-format input: one lane per distinct value of this column. Unset, every non-time column is its own lane.
stateColumnstringfirst remaining columnStatus column for long-format input. Ignored without seriesColumn.
showValuesbooleanfalseRender the status text inside cells wide enough to fit it.
showLegendbooleantrueStatus color legend below the lanes.
colorsobject{}Fixed status → CSS color mapping (e.g. { pass: "#22c55e" }); unmapped statuses cycle the shared palette.
rowHeightnumber0.9Cell height as a fraction of the lane height (0.2–1).
colWidthnumber0.9Cell width as a fraction of the sampling-interval slot (0.2–1) — lower values add horizontal breathing room between cells.

Heatmap

A time × bucket grid of color-intensity cells — request-duration histograms over time, status-code distributions, per-entity activity. Supports drag-to-zoom (writes the new range to the URL). Data shape: see Heatmap.

Cells are opaque — a cell at the domain minimum gets the ramp's low-end color, while a cell with no rows draws nothing (the panel background shows through).

plugin:
  kind: Heatmap
  spec:
    yColumn: duration_bucket
    valueColumn: requests
    unit: req
    colorScheme: spectral
    scaleType: log
OptionTypeDefaultNotes
yColumnstringfirst non-time columnY-bucket column.
valueColumnstringfirst remaining numeric columnCell intensity column.
unitstring""Value formatting in cells, tooltip and legend.
showLegendbooleantrueColor ramp legend (min → max) below the grid.
showValuesbooleanfalseRender the value inside cells wide enough to fit it.
colorSchemeenumspectralCell color ramp. Multi-hue: spectral (cool blue → yellow → hot red), greenYellowRed. Single-hue light→dark: blues, greens, oranges, reds.
scaleTypeenumlinearValue→color curve. sqrt lifts the low end; log spreads heavily skewed data (e.g. histogram counts) so sparse cells stay visible.
minnumber0 (or the data minimum if negative)Lower bound of the color domain — cells at or below it clamp to the ramp's low-end color.
maxnumberdata maximumUpper bound of the color domain. Set it (e.g. an expected ceiling) for colors that don't shift with the time window.
cellGapnumber1Gap between cells in px (0–4).

NodeGraph

A directed graph of nodes and weighted edges laid out with a deterministic force-directed layout — service dependency maps, call graphs, data flows. Edge thickness tracks the edge's weight; hovering a node highlights its neighborhood, and hovering a node or edge shows a tooltip with the value and (for nodes) the in/out edge counts. Data shape: see Node graph.

plugin:
  kind: NodeGraph
  spec:
    sourceColumn: client
    targetColumn: server
    valueColumn: calls
    unit: req
    directed: true
OptionTypeDefaultNotes
sourceColumnstringsourceEdge source column; falls back to the first column when absent.
targetColumnstringtargetEdge target column; falls back to the second column when absent.
valueColumnstringvalueEdge weight column — drives edge thickness and node size. Falls back to the first remaining numeric column; without one every edge weighs 1.
unitstring""Value formatting in tooltips and edge labels.
directedbooleantrueDraw arrowheads pointing at each edge's target.
showValuesbooleanfalseRender the edge's value at its midpoint.
maxNodesnumberCap the node count (≥ 2): the highest-value nodes stay, the rest (and their edges) are hidden behind the "not shown" badge. The layout also has a built-in 250-node limit.

Shaping data for each visualization

What you SELECT determines what renders. The visualizations infer their structure from your columns:

Time series

  • One column must be the time axis. It's detected by exact name (case-insensitive) — alias it to ts, time, or timestamp.
  • Remaining numeric columns become series (lines).
  • A remaining string column is treated as a series label, pivoting one value column into multiple lines. For example, grouping by ServiceName:
SELECT toStartOfInterval(Timestamp, INTERVAL {step:UInt32} SECOND) AS ts,
       ServiceName,
       count() AS spans
FROM traces
WHERE Timestamp >= {from:String} AND Timestamp <= {to:String}
GROUP BY ts, ServiceName
ORDER BY ts

This produces one line per service.

The label column must be a non-numeric string. ClickHouse returns 64-bit integers and string columns identically in its JSON output, so a label cast to a numeric string — e.g. toString(StatusCode) — can't be told apart from a value column and renders as its own line instead of pivoting. Make the label unambiguous, for example concat('status-', toString(StatusCode)).

Bar

Same column rules as the time series — a time column (ts/time/timestamp) makes one bar group per bucket, numeric columns become series, and a remaining string column pivots a single value column into one series per label. Without a time column the first string column becomes the category axis instead:

SELECT ServiceName, count() AS spans
FROM traces
WHERE Timestamp >= {from:String} AND Timestamp <= {to:String}
GROUP BY ServiceName
ORDER BY spans DESC
LIMIT 10

This produces one bar per service, in the query's order. Multiple queries merge onto one axis.

Table

Renders every column as-is, in query order. No special column naming is required — return whatever you want to display:

SELECT ServiceName, count() AS spans, round(avg(Duration) / 1e6, 1) AS avg_ms
FROM traces
WHERE Timestamp >= {from:String} AND Timestamp <= {to:String}
GROUP BY ServiceName
ORDER BY spans DESC
LIMIT 50

With multiple queries the panel shows a per-query selector.

Geo map

GeoMap takes latitude/longitude rows in points mode or region rows in choropleth mode (the column names are options — see GeoMap). In points mode it renders all query frames, giving each query its own marker color; in choropleth mode it merges every frame by combining the value of each region with the configured aggregation (sum by default). Region codes that don't resolve to a known country and points with invalid coordinates are dropped — when any rows are skipped, the panel shows a small "N rows not mapped" badge in its top-right corner.

Treemap

Treemap takes one label column and one positive numeric column; rows sharing the same label are summed. Rows with a missing label or a non-positive value can't be tiled and are dropped — when any are, the panel shows a small "N rows not shown" badge in its top-right corner.

State timeline

StateTimeline needs the same time column as a time series (alias to ts, time, or timestamp). Two data shapes are supported. Wide (the default): every non-time column is its own lane, with the cell value as the state. Long: set seriesColumn (and optionally stateColumn) in the spec to pivot GROUP BY-style rows into one lane per label:

SELECT toStartOfInterval(Timestamp, INTERVAL {step:UInt32} SECOND) AS ts,
       ServiceName AS service,
       if(countIf(StatusCode = 'Error') > 0, 'error', 'ok') AS status
FROM traces
WHERE Timestamp >= {from:String} AND Timestamp <= {to:String}
GROUP BY ts, service
ORDER BY ts

Each state holds until the lane's next sample (the last one holds to the end of the range), so emit a row per bucket — or only on state changes — and return NULL for "no data" gaps.

Status history

StatusHistory takes the same data shapes as StateTimeline (wide or long, same seriesColumn/stateColumn pivot) but with the opposite sample semantics: each sample is an independent observation drawn as one discrete cell, nothing holds until the next sample, and a missing sample stays visibly empty. Reach for it when each row is a periodic check or run (health probes, cron jobs, CI builds per bucket) and "did it run, and what did it return?" matters; reach for StateTimeline when rows are state transitions and duration matters.

Each sample renders as a fixed-width cell centered on its timestamp, sized to the sampling interval (the smallest gap between distinct timestamps), so a missing sample shows as a visible empty slot. A NULL status also draws nothing.

Heatmap

Heatmap needs the same time column (alias to ts, time, or timestamp), plus a bucket column for the y-axis and a numeric column for the cell intensity — GROUP BY time and bucket:

SELECT toStartOfInterval(Timestamp, INTERVAL {step:UInt32} SECOND) AS ts,
       multiIf(Duration < 1e7, '<10ms',
               Duration < 1e8, '<100ms',
               Duration < 1e9, '<1s', '≥1s') AS bucket,
       count() AS requests
FROM traces
WHERE Timestamp >= {from:String} AND Timestamp <= {to:String}
GROUP BY ts, bucket
ORDER BY ts

Rows that land on the same (time, bucket) cell are summed — within a query and across queries. Buckets that are all numeric (histogram bounds) sort like a y-axis with the largest at the top; categorical buckets keep their first-seen order top-down.

Node graph

NodeGraph takes an edge list — one row per edge with a source column, a target column, and an optional numeric weight (no time axis; aggregate over the window and LIMIT):

SELECT ServiceName AS source,
       PeerService AS target,
       count() AS value
FROM traces
WHERE Timestamp >= {from:String} AND Timestamp <= {to:String}
  AND PeerService != ''
GROUP BY source, target
ORDER BY value DESC
LIMIT 200

Nodes are derived from the distinct endpoints, sized by the total weight flowing through them. Rows with the same (source, target) pair are summed — within a query and across queries — and the reverse direction is a separate edge (drawn offset so both stay visible). Rows missing either endpoint, and self-loops, are dropped — when any are, the panel shows a small "not shown" badge in its top-right corner. Without a numeric column every edge weighs 1, so a plain SELECT client, server edge list still sizes by row count.

Stat and gauge

StatChart and GaugeChart share the same data shape: each reduces a numeric column to a single value via a calculation (last, mean, max, …) — stat shows it as a big number, gauge as a filled arc between min and max. Return one numeric column (the stat's optional sparkline follows the time column, or row order without one):

SELECT toStartOfInterval(Timestamp, INTERVAL {step:UInt32} SECOND) AS ts, count() AS requests
FROM traces
WHERE Timestamp >= {from:String} AND Timestamp <= {to:String}
GROUP BY ts ORDER BY ts

Multiple resulting series render as multiple tiles or gauges; a query that returns no value renders its tile (or an empty gauge) with the noValue text instead of disappearing.

Time range parameters

The dashboard's global time range (the picker at the top, stored in the URL) is supplied to every query as two ClickHouse parameters, {from:String} and {to:String} — UTC datetimes in YYYY-MM-DD HH:MM:SS.mmm form. They are not injected automatically, so add them to your WHERE clause to scope the query to the picker:

WHERE Timestamp >= {from:String} AND Timestamp <= {to:String}

A query that omits this scans all of history and won't follow the picker — time-series panels still clamp to the visible range, but Table and StatChart aggregate everything and stop matching the selected range. The parameters are always provided, so referencing them never errors.

Adaptive bucketing

Alongside from/to, every query also receives {step:UInt32} — a bucket width in seconds, sized from the selected range so the series yields roughly 500 points at any zoom level. Use it instead of hard-coding a resolution like toStartOfMinute, which over- or under-buckets the moment the picker moves:

SELECT toStartOfInterval(Timestamp, INTERVAL {step:UInt32} SECOND) AS ts,
       count() AS spans
FROM traces
WHERE Timestamp >= {from:String} AND Timestamp <= {to:String}
GROUP BY ts ORDER BY ts

The width snaps to clock-aligned values so bucket boundaries land on clean axis lines — see Query parameters for the exact widths. Like from/to, the parameter is always bound; queries that don't reference it are unaffected.

The first and last buckets of a range are usually partial — the picker's edges rarely fall on a bucket boundary, so the leading bucket starts before from and the trailing one ends before a full step has elapsed. An aggregate like count() or sum() therefore reads lower at the edges: the time-series chart clips the leading partial bucket at the left edge and shows the trailing one as a dip. This is inherent to time bucketing (Grafana behaves the same) — read edge values as approximate, and don't mistake the trailing dip for a real drop.

You can also reference dashboard variables with $name tokens, which Everr interpolates server-side before running the query.

TestData query kind (development / test)

TestData is a synthetic, deterministic query source for development and visual testing — it returns generated rows without touching ClickHouse, so a dashboard renders stable output independent of real telemetry. It powers the Visualization Gallery. It is not meant for product dashboards.

A query selects it with kind: TestData and a scenario. It reuses the panel's resolved time range and adaptive {step}, so time-series scenarios span [from, to] exactly like a real query. Generation is deterministic given seed.

random_walk

Numeric time series (or row-indexed series) — for charts and stat tiles.

FieldDefaultMeaning
serieslist of { name, start, noise, min?, max?, nullChance?, round? }; one numeric column each
seed1deterministic generation
labelColumnwhen set, emit long output (ts, <labelColumn>, <valueColumn>) → one series per label (grouped pivot)
valueColumnvaluevalue column name for long output
timeColumntruefalse omits ts and emits points rows (row-order sparkline)
points50row count when timeColumn: false

table

Generated tabular rows. rows: 0 yields an empty frame.

Column fieldMeaning
time: truetimestamp spread across [from, to]
values: [...]cycled categorical values (may include null)
walk: { start, noise, min?, max?, round? }numeric random walk
seq: trueascending integer (1-based)
const: <value>constant for every row

csv

Literal rows, verbatim. rows: [] yields an empty frame. Values are positional, aligned to columns, and may be null.

- kind: TestData
  spec:
    plugin:
      kind: TestData
      spec:
        scenario: csv
        columns: [severity, logs]
        rows:
          - [ERROR, 12]
          - [WARN, 48]

geo

Generated geographic rows for the GeoMap visualization. shape: points emits lat/lon/value rows; shape: regions emits region/value rows.

FieldDefaultMeaning
shapepointspoints → lat/lon/value rows; regions → region/value rows
seed1deterministic generation
points20number of point rows (points shape)
count12number of region rows (regions shape)
latColumnlatlatitude column name (points shape)
lonColumnlonlongitude column name (points shape)
regionColumnregionregion column name (regions shape)
valueColumnvaluevalue column name
- kind: TestData
  spec:
    plugin:
      kind: TestData
      spec:
        scenario: geo
        shape: points
        points: 30
        seed: 7
- kind: TestData
  spec:
    plugin:
      kind: TestData
      spec:
        scenario: geo
        shape: regions
        count: 20

Unknown visualizations

If a panel's plugin.kind isn't a registered visualization, the panel renders a clear "Unknown visualization: <kind>" placeholder instead of failing the whole dashboard. Stick to TimeSeriesChart, BarChart, Table, StatChart, GaugeChart, GeoMap, Treemap, StateTimeline, StatusHistory, Heatmap, and NodeGraph.