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.
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:
- A panel type — how the data is drawn (line / number / pie / table)
- A query — what data fills it (built with the visual Query Builder, raw PromQL, or pasted JSON)
- 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.
| Panel | Best for | Required fields | Notes |
|---|---|---|---|
| Time Series | Trends over time — request rates, latency percentiles, resource usage | One or more queries; metric name per query | The only panel that supports PromQL mode. Up to 5 series returned per PromQL query, 100 per builder query. |
| Query Value | A single KPI — total requests, current users, error budget | Exactly one query, single-series result | Limit must resolve to 1 series — no group-by, or a group-by that collapses to one row. |
| Pie Chart | Distribution / share — "who's making the noise?" | One query + exactly one group-by label | The Run button errors with "Query A requires exactly one Group By label for pie widgets" if missing — see the validation rules. |
| Table | Multi-metric rows — "CPU + memory + restarts per container" | At least one group-by label and one column | Up 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.
| Mode | Available on | When to use |
|---|---|---|
| Query Builder | Time Series · Query Value · Pie · Table | The default — visual, no PromQL syntax to learn. Compiles to PromQL behind the scenes. |
| JSON | Time Series · Query Value · Pie · Table | Paste / edit the raw widget config. Useful for templating widgets across dashboards or replicating something exactly. |
| PromQL | Time Series only | When 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 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 type | What it represents | Time aggregations that fit | Examples |
|---|---|---|---|
| Counter | Monotonically increasing total (resets on container restart). Always ends in _total. | rate, increase, irate — convert the raw counter into a per-second / per-window value | container_http_requests_total, container_log_messages_total, container_resources_cpu_usage_seconds_total |
| Gauge | A point-in-time value that can go up or down | avg_over_time, min_over_time, max_over_time, last_over_time | container_resources_memory_rss_bytes, container_resources_disk_used_bytes, container_jvm_heap_used_bytes |
| Histogram | A 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 mode | Latency distributions, response-size distributions |
rate() to a gaugeThe 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:
| Template | Pattern | Swap A/B available | When to reach for it |
|---|---|---|---|
| Sum | A + B | — | Total of two streams (HTTP + gRPC requests, reads + writes) |
| Difference | A − B | ✓ | Gap between two metrics (capacity − usage, allocated − consumed) |
| Ratio | A / B | ✓ | Per-unit metrics (latency per request, errors per second-of-uptime) |
| Multiply | A × 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 (
AandB) - 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:
| Flow | Where it lives | What it does |
|---|---|---|
| Bulk import | Edit mode → JSON button at top right opens the "Add multiple widgets" dialog | Paste a { "widgets": [...] } JSON array and append all of them at once. Each widget is validated independently; failures don't reject the batch. |
| Per-widget JSON | Open a widget → switch to the JSON tab inside the editor | Edit 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.
| Limit | Value |
|---|---|
| Widgets per dashboard | 50 |
| Widgets per bulk-import request | 10 |
| Dashboard versions kept | 5 (rolling) |
| Bookmarked versions per dashboard | 10 (shared) |
| Filters per query | 5 |
| Returned series — builder/table queries | 100 |
| Returned series — PromQL queries | 5 |
| Top-K values — most builder queries | 20 |
| Top-K values — time series | 5 |
| Table columns | 5 |
| Dashboard name | 1–50 chars |
| Dashboard description | 0–300 chars |
| Tags per dashboard | 0–20 |
| Tag length | 1–32 chars, [A-Za-z0-9-]+ only |
| Widget title | 1–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_idspan_idrequest_idsession_idpod_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:
| Action | Who needs it |
|---|---|
CustomDashboards.Edit | Create, update, delete, add/edit widgets, bulk import |
CustomDashboardsView.View | List, 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