Skip to main content

Custom Dashboards — Overview

Custom Dashboards let you assemble your own panels from the same telemetry that powers every built-in CodeXray view. Drop a Time Series chart for trends, a Query Value tile for a KPI, a Pie for distribution, a Table for detail — all on a draggable grid, all scoped to one tenant + one project, all backed by the same metric store CodeXray is already collecting from your agents.

Custom Dashboards vs. the built-in views

The Applications, Topology, Nodes, and Database Insights views are opinionated — CodeXray decides what to show. Custom Dashboards flip that: you decide. Use them for KPIs, executive rollups, service-team boards, capacity reviews, anything CodeXray doesn't draw out of the box.


The 60-Second Tour​

A quick fast-forward of the whole flow — create a dashboard, build a Time Series widget with a formula, watch the validation error when something's off, save, and you're live.

The rest of this page is the reference. The next two pages — Managing Dashboards and Building Widgets — are the operator's manuals for what you just watched.


What You Can Build​

A dashboard is a named grid of widgets scoped to your project. Every widget combines three things:

  1. A panel type — how the data is drawn (line / number / pie / table)
  2. A query — what data fills it (built with the visual Query Builder, raw PromQL, or pasted JSON)
  3. A style + settings block — title, description, time range, sometimes a formula

Dashboards carry their own metadata too: a type (Custom · Service · Infrastructure · Business · SRE · Security), free-text tags for filtering, and a star to pin favourites to the top of the list.


The Four Panel Types​

Each type renders data differently and enforces slightly different query rules.

PanelBest forRequired fieldsNotes
Time SeriesTrends over time — request rates, latency percentiles, resource usageOne or more queries; metric name per queryThe only panel that supports PromQL mode. Up to 5 series returned per PromQL query, 100 per builder query.
Query ValueA single KPI — total requests, current users, error budgetExactly one query, single-series resultLimit must resolve to 1 series — no group-by, or a group-by that collapses to one row.
Pie ChartDistribution / share — "who's making the noise?"One query + exactly one group-by labelThe Run button errors with "Query A requires exactly one Group By label for pie widgets" if missing — see the validation rules.
TableMulti-metric rows — "CPU + memory + restarts per container"At least one group-by label and one columnUp to 5 columns; each column can be its own query with its own aggregation.

For an at-a-glance comparison of which features each panel exposes, see Building Widgets — Panel-by-panel rules.


The Three Query Modes​

Every widget's data comes from one of three modes. Time Series widgets can use all three. Query Value / Pie / Table widgets can use the first two only.

ModeAvailable onWhen to use
Query BuilderTime Series · Query Value · Pie · TableThe default — visual, no PromQL syntax to learn. Compiles to PromQL behind the scenes.
JSONTime Series · Query Value · Pie · TablePaste / edit the raw widget config. Useful for templating widgets across dashboards or replicating something exactly.
PromQLTime Series onlyWhen you need a derived metric, complex multi-metric arithmetic, or a histogram quantile that the builder doesn't expose. Time-series-only because the result must be a vector over time.
PromQL is intentionally Time Series only

PromQL produces a vector over time — which is what a line chart wants. A pie or single-value widget needs a scalar; a table needs labelled rows. The builder mode resolves those shapes for non-time-series panels in a way raw PromQL can't, so PromQL is hidden when the panel type isn't Time Series.


Metric Types Drive Aggregation Choices​

CodeXray emits three Prometheus metric types — counter, gauge, and histogram. The Query Builder picks sensible aggregations based on the type you query, but knowing which is which makes the choice obvious:

Metric typeWhat it representsTime aggregations that fitExamples
CounterMonotonically increasing total (resets on container restart). Always ends in _total.rate, increase, irate — convert the raw counter into a per-second / per-window valuecontainer_http_requests_total, container_log_messages_total, container_resources_cpu_usage_seconds_total
GaugeA point-in-time value that can go up or downavg_over_time, min_over_time, max_over_time, last_over_timecontainer_resources_memory_rss_bytes, container_resources_disk_used_bytes, container_jvm_heap_used_bytes
HistogramA distribution of observations bucketed by upper bound. Backed by _bucket, _sum, _count series.histogram_quantile() on top of a rate() of buckets — typically only reachable from PromQL modeLatency distributions, response-size distributions
Don't apply rate() to a gauge

The most common mistake is putting a gauge through rate() — rate() of memory bytes gives you the change rate of memory, almost never what you want. The builder's metric-type field (Counter / Gauge in each query row) defaults the aggregation to the right family; only override when you know why.

The "Aggregate within time series" dropdown handles the time aggregation above. The "Aggregate across time series" dropdown handles the space aggregation — how to fold multiple series into one (sum / avg / min / max / count / topk). See Building Widgets — Aggregations for the deep dive.


Formulas — Math Between Queries​

Add two queries (A and B) and the builder lets you combine them with a formula. The Formula bar offers seven one-click templates — pick a template, the expression is filled in automatically, and you tweak from there if needed:

TemplatePatternSwap A/B availableWhen to reach for it
SumA + B—Total of two streams (HTTP + gRPC requests, reads + writes)
DifferenceA − B✓Gap between two metrics (capacity − usage, allocated − consumed)
RatioA / B✓Per-unit metrics (latency per request, errors per second-of-uptime)
MultiplyA × B—Combined scaling (requests × average cost)
Percentage(A / B) × 100✓A as a percentage of B (5xx errors / all requests, GC pause / wall time)
Average(A + B) / 2✓Mean of two metrics (smoothing two probes of the same thing)
Percent Change((A − B) / B) × 100✓Growth rate (current period vs prior period)

Each formula:

  • References up to two metrics (A and B)
  • Supports the four basic operators in the underlying expression: +, −, *, /
  • Has a swap icon on the templates marked ✓ — useful when you've put the queries in the wrong order
  • Shows a live preview of the resolved expression with your actual metric names below the bar

The expression field is editable, so once you've picked a template you can adjust it freely (e.g. start with Percentage and edit to (A / B) * 100 - 1 if that's what you actually want).

See Building Widgets — Formulas for the full flow with screenshots.


Per-Widget Time Range​

Unlike many dashboard tools where time is dashboard-wide, every CodeXray widget carries its own time preference: 5 minutes, 15 minutes, 30 minutes, 1 hour, 2 hours, or 3 hours. This lets a single dashboard mix "last 5 minutes" live KPIs with "3-hour" trend charts side-by-side.

The time preference is configured in the widget editor's right-side panel under Widget Time Preference.


JSON Import / Export​

Two flows let you move widgets between dashboards or version them in Git:

FlowWhere it livesWhat it does
Bulk importEdit mode → JSON button at top right opens the "Add multiple widgets" dialogPaste a { "widgets": [...] } JSON array and append all of them at once. Each widget is validated independently; failures don't reject the batch.
Per-widget JSONOpen a widget → switch to the JSON tab inside the editorEdit one widget's raw config. Useful for tiny tweaks that are awkward in the builder.

The bulk import is capped at 10 widgets per request (server-enforced). For larger transfers, paste twice.


Version History​

Dashboards are versioned automatically. Every save is kept as a snapshot, and you can roll back to any of the last 5 versions — or bookmark an important one to keep it restorable for good. It's a built-in safety net for shared boards: an unwanted change is one click to undo. See Managing Dashboards — Version History.


Limits and Constraints​

Every limit below is enforced server-side — the UI usually surfaces them as inline errors, but knowing the cap up front saves a round-trip.

LimitValue
Widgets per dashboard50
Widgets per bulk-import request10
Dashboard versions kept5 (rolling)
Bookmarked versions per dashboard10 (shared)
Filters per query5
Returned series — builder/table queries100
Returned series — PromQL queries5
Top-K values — most builder queries20
Top-K values — time series5
Table columns5
Dashboard name1–50 chars
Dashboard description0–300 chars
Tags per dashboard0–20
Tag length1–32 chars, [A-Za-z0-9-]+ only
Widget title1–80 chars
Display-text pattern^[A-Za-z0-9 _.,:();\-]+$

Blocked Filter Labels​

Five labels can never be used as filters or group-by keys in a custom dashboard query — they're high-cardinality identifiers that would blow up the time-series store if grouped on:

  • trace_id
  • span_id
  • request_id
  • session_id
  • pod_uid

If you try to filter on one of these, the validator will reject the query. Use them as trace search criteria instead — they're optimised for that path.


Permissions​

Custom Dashboards are scoped to a tenant + project and gated by two RBAC actions:

ActionWho needs it
CustomDashboards.EditCreate, update, delete, add/edit widgets, bulk import
CustomDashboardsView.ViewList, open, run queries, view results

A user with View only can browse and read every dashboard in the project but can't modify anything — the Edit button, JSON button, and + Add widget button don't render for them. See Role-Based Access Control for how to assign these.


Next Steps​

  • Managing Dashboards — the list view, creating dashboards, view vs edit mode, drag-resize layout, JSON import
  • Building Widgets — the editor anatomy, the three query modes, the Query Builder deep dive, per-panel-type required fields, metric types and aggregations, formulas
  • Architecture — where the dashboard engine sits in the full CodeXray pipeline