Introduction
Grafatui is a terminal user interface for Prometheus dashboards. It is designed for fast inspection, SSH sessions, local debugging, and environments where opening a browser-based Grafana instance is inconvenient.
Grafatui reads Prometheus directly and can import Grafana dashboard JSON files. It renders supported panels as terminal charts, tables, gauges, stats, and heatmaps while keeping the workflow keyboard-first.
When Grafatui Fits
Use Grafatui when you want:
- A lightweight Prometheus dashboard in your terminal.
- A familiar way to inspect exported Grafana dashboards.
- Fast startup and low resource usage.
- A dashboard that works well over SSH.
- SVG or PNG snapshots of the current TUI view.
Grafatui is not a Grafana server replacement. It does not manage users, alerts, annotations, dashboard editing, plugins, or browser-only visualizations.
Project Links
Installation
Homebrew
Install Grafatui with Homebrew on macOS or Linux:
brew install fedexist/grafatui/grafatui
Installer Script
Install the latest prebuilt release without requiring Rust:
bash -o pipefail -c 'curl --proto =https --tlsv1.2 -LsSf https://raw.githubusercontent.com/fedexist/grafatui/main/install.sh | bash'
With wget:
bash -o pipefail -c 'wget -O- https://raw.githubusercontent.com/fedexist/grafatui/main/install.sh | bash'
The script supports Linux and macOS on x86_64 and ARM64. It installs to
$HOME/.local/bin and never invokes sudo. Make sure that directory is on
your PATH.
Set GRAFATUI_INSTALL_DIR to choose another destination:
bash -o pipefail -c 'curl --proto =https --tlsv1.2 -LsSf https://raw.githubusercontent.com/fedexist/grafatui/main/install.sh | GRAFATUI_INSTALL_DIR=/custom/bin bash'
Set GRAFATUI_VERSION to install a specific release. The leading v is
optional:
bash -o pipefail -c 'curl --proto =https --tlsv1.2 -LsSf https://raw.githubusercontent.com/fedexist/grafatui/main/install.sh | GRAFATUI_VERSION=v0.1.11 bash'
Every release download is verified against its published SHA-256 checksum manifest. Installation stops if the manifest is unavailable or verification fails.
Reviewing downloaded scripts before running them is recommended:
curl --proto '=https' --tlsv1.2 -LsSf -o install.sh https://raw.githubusercontent.com/fedexist/grafatui/main/install.sh
less install.sh
bash install.sh
From Crates.io
Install the latest published release with Cargo:
cargo install grafatui
Grafatui currently requires Rust 1.88 or newer.
From Source
Clone the repository and install the local checkout:
git clone https://github.com/fedexist/grafatui.git
cd grafatui
cargo install --path .
For development, use cargo run instead:
cargo run -- --prometheus-url http://localhost:9090
Prebuilt Binaries
Prebuilt release assets are published on GitHub Releases for common Linux, macOS, and Windows targets.
Shell Completions
Grafatui can generate shell completions for Bash, Zsh, Fish, PowerShell, and Elvish.
# Bash
source <(grafatui completions bash)
# Zsh
source <(grafatui completions zsh)
# Fish
grafatui completions fish | source
Man Page
Generate a man page from the CLI definition:
grafatui man > grafatui.1
Quick Start
Connect to Prometheus
If Prometheus is already running locally:
grafatui --prometheus-url http://localhost:9090
Point Grafatui at another Prometheus server with the same option:
grafatui --prometheus-url http://prometheus.example.com:9090
Import a Grafana Dashboard
Grafatui imports either a Classic JSON dashboard or an exact
dashboard.grafana.app/v2 JSON resource that uses recursive grid, row, tab, or AutoGrid
containers:
grafatui --prometheus-url http://localhost:9090 --grafana-json ./dashboard.json
For V2 dashboards that use repeated rows or tabs, row/tab conditional rendering, or library panels, use the Classic export fallback: open Export as code → Advanced options, set Model to Classic, then download or copy the JSON. V2 resources can be imported as JSON or YAML; V1 Resource files are unsupported. See Grafana Dashboard Import for the full format requirements.
Override dashboard variables with repeated --var options:
grafatui --grafana-json ./dash.json --var job=node --var instance=server-01
Run the Demo
The repository includes a Prometheus demo stack and sample dashboards:
git clone https://github.com/fedexist/grafatui.git
cd grafatui
cd examples/demo && docker-compose up -d && sleep 5 && cd ../..
cargo run -- --grafana-json examples/dashboards/prometheus_demo.json --prometheus-url http://localhost:19090
When finished:
cd examples/demo
docker-compose down -v
Useful First Keys
| Key | Action |
|---|---|
q | Quit |
r | Force refresh |
+ / - | Zoom out / in |
[ / ] | Pan left / right |
f | Fullscreen selected panel |
Enter / Space on a row | Toggle the row |
Left / Right on a row | Collapse / expand the row |
v | Inspect values |
/ | Search visible rows and panels |
Try bounded AutoGrid tables with:
grafatui --grafana-json examples/dashboards/grafana_v2_autogrid_bounded.json \
--prometheus-url http://localhost:9090
Use Ctrl+Up/Ctrl+Down to scroll a selected capped table, and Ctrl+Home/
Ctrl+End to reach its first/last rows. Press f for fullscreen when a tiny
cap leaves no room for data. See keyboard and mouse controls.
Configuration
Grafatui can be configured with CLI options, a TOML configuration file, or both. CLI options override values from the configuration file.
Common CLI Options
| Option | Description | Default |
|---|---|---|
--prometheus-url <URL> | Prometheus server URL | http://localhost:9090 |
--ca-cert <FILE> | PEM CA certificates to trust in addition to the built-in public roots | none |
--client-cert <FILE> | PEM client certificate chain for mutual TLS; requires --client-key or its config equivalent | none |
--client-key <FILE> | PEM private key for the mutual TLS client certificate | none |
--grafana-json <FILE> | Grafana dashboard file: Classic JSON, or V2 resource JSON or YAML | none |
--annotations-file <FILE> | Read-only external JSONL point-event file | none |
--annotations-command <PROGRAM> | Read-only executable annotation provider | none |
--annotations-command-arg <ARG> | Argument for --annotations-command; repeat to preserve order | none |
--annotations-command-timeout <DURATION> | Maximum command-provider runtime | 10s |
--validate | Check the Grafana dashboard import and exit without starting the TUI | false |
--strict | Make --validate fail when diagnostics contain warnings | false |
--format <FORMAT> | Output format for --validate: text or json | text |
--range <DURATION> | Time range window, such as 5m, 1h, or 24h | 5m |
--step <DURATION> | Exact query step, such as 5s or 30s; omit it for automatic range-aware resolution | automatic, minimum 5s |
--scrape-interval <DURATION> | Prometheus scrape interval, used for $__rate_interval | 15s |
--var <KEY=VALUE> | Override a dashboard variable | none |
--theme <NAME> | UI theme | default |
--threshold-marker <MARKER> | Marker for threshold lines | dashed |
--autogrid-color <COLOR> | Color for automatic graph grid lines and labels | dark-gray |
--export-dir <DIR> | Directory for exports and recordings | ./grafatui-exports |
--export-format <FORMAT> | svg, png, or both | svg |
--record-max-frames <COUNT> | Maximum changed frames per recording | 300 |
--refresh-rate <MS> | Data fetch interval in milliseconds | 1000 |
--config <FILE> | Configuration file path | none |
Run the full help output with:
grafatui --help
Configuration File
Create grafatui.toml in ~/.config/grafatui/, or pass a custom path with --config.
prometheus_url = "http://localhost:9090"
refresh_rate = 1000
time_range = "1h"
step = "5s"
scrape_interval = "15s"
theme = "dracula"
threshold_marker = "dashed"
export_dir = "./grafatui-exports"
export_format = "svg"
record_max_frames = 300
autogrid = true
autogrid_color = "dark-gray"
grafana_json = "~/.config/grafatui/my-dashboard.json"
annotations_file = "./events.jsonl"
[vars]
job = "node"
instance = "server-01"
Private CAs and Mutual TLS
TLS settings are optional. Without them, plain HTTP and public HTTPS behave as before. To connect to a Prometheus server using a private CA and mutual TLS:
prometheus_url = "https://prometheus.internal:9090"
[tls]
ca_cert = "~/.config/grafatui/tls/ca.pem"
client_cert = "~/.config/grafatui/tls/client.pem"
client_key = "~/.config/grafatui/tls/client.key"
The equivalent CLI options are:
grafatui --prometheus-url https://prometheus.internal:9090 \
--ca-cert ca.pem \
--client-cert client.pem \
--client-key client.key
ca_certmay contain multiple PEM CA certificates. They augment the built-in public roots rather than replacing them.client_certcontains the client certificate followed by any intermediate certificates.client_keyaccepts an unencrypted RSA, SEC1 EC, or PKCS#8 PEM private key. The two options must be set together.- CLI values override matching
[tls]values one by one. Paths beginning with~are expanded after precedence is resolved. - TLS options require an
https://Prometheus URL. There is no insecure certificate-verification bypass. - Unknown
[tls]keys are rejected. File and parsing errors identify the option and path without including certificate or private-key contents.
Query Resolution
When step/--step is absent, range queries choose their step the way
Grafana’s Prometheus datasource does:
- Divide the time range by the panel’s
maxDataPoints, or by 1000 when it is unset. - Round to a Grafana interval, such as
20s,1m, or10m. - Use the min interval instead when it is larger. The min interval is the
target’s
interval, then the panel’sinterval, then5s. - Coarsen the step if the query would exceed Prometheus’s limit of 11,000 points per series.
An explicit --step or configured step is used exactly. It is not rounded,
coarsened for the time range, or overridden by imported panel/target intervals.
$__interval is the step a query runs with. $__rate_interval is
max($__interval + scrape interval, 4 × scrape interval). The scrape interval
is the query’s min interval in automatic mode when the dashboard sets one, and
--scrape-interval otherwise. Explicit-step mode always uses
--scrape-interval for this calculation.
With the defaults, a 5m or 1h range queries every 5s, 24h every 1m,
and 7d every 10m. The title bar shows the step for panels without their own
query options.
Prometheus Request Limits and Retries
Grafatui reads at most 64 MiB from one Prometheus response and keeps at most 64 complete range-query windows in its in-memory cache. Identical in-flight range queries share one request; cancelling their leader releases waiting callers so a later refresh can retry normally.
Transport failures, HTTP 429, and HTTP 5xx responses are retried with the
existing bounded backoff. Rejected HTTP 4xx queries, malformed responses, and
responses over the 64 MiB limit are not retried. Error excerpts are bounded and
remain valid UTF-8.
External Annotation Sources
Select one read-only annotation source: annotations_file or the nested
[annotations_command] table. The two TOML forms conflict. The
--annotations-file CLI flag conflicts with every command-source CLI flag;
CLI source selection still replaces the complete TOML annotation source.
[annotations_command]
program = "./target/debug/examples/git_annotation_provider"
args = ["."]
timeout = "10s"
program is required; args defaults to an empty list and timeout defaults
to 10s. The matching CLI source is:
grafatui \
--annotations-command ./target/debug/examples/git_annotation_provider \
--annotations-command-arg=. \
--annotations-command-timeout 10s
--annotations-command-arg and --annotations-command-timeout require
--annotations-command; repeat the argument flag to retain argument order.
--annotations-file and --annotations-command cannot be combined. A CLI file
or command has whole-source precedence over TOML: it replaces the configured
file or complete command configuration rather than merging individual fields.
Themes
Built-in themes include:
defaultdraculamonokaisolarized-darksolarized-lightgruvboxtokyo-nightcatppuccin
Use a theme from the CLI:
grafatui --theme tokyo-night
External Annotations
Grafatui can overlay read-only, external point events from exactly one source:
a JSONL file or a command provider. It never edits or writes either source.
External annotations are deliberately separate from Grafana dashboard
annotations: Grafatui does not implement Grafana annotation queries, APIs,
annotations, or annotations.list.
Enable Annotations
Select exactly one source. For a file source, pass the path on the command line:
grafatui \
--grafana-json ./dashboard.json \
--annotations-file ./events.jsonl
Or configure the file source in TOML:
annotations_file = "./events.jsonl"
For a command source, configure an executable that accepts the request protocol below. The command receives no shell interpolation:
[annotations_command]
program = "./target/debug/examples/git_annotation_provider"
args = ["."]
timeout = "10s"
Or select it from the command line:
grafatui \
--grafana-json ./dashboard.json \
--annotations-command ./target/debug/examples/git_annotation_provider \
--annotations-command-arg=.
Annotation files larger than 16 MiB are rejected with a warning, and at most 100,000 events, the newest, are kept from either kind of source.
File and command sources are mutually exclusive. A TOML configuration that sets both is rejected even if the CLI selects a source. A CLI file or command replaces the complete TOML annotation source; it never mixes a CLI program, arguments, or timeout with TOML values. Sources are opt-in and read-only; Grafatui does not create, edit, or otherwise write them.
Command Provider Protocol
Grafatui writes exactly one version-1 request line to the command’s standard input, then closes stdin. The request defines the complete refresh window:
{"version":1,"range":{"from":"2026-08-12T10:00:00Z","to":"2026-08-12T10:05:00Z"}}
range.from and range.to are inclusive UTC RFC 3339 bounds. Grafatui
defensively applies its visible-range filtering to the events returned.
The provider writes zero or more existing JSONL events to stdout and diagnostics
to stderr. Exit 0 with valid bounded JSONL replaces the complete annotation
snapshot; an empty successful stdout clears it. A spawn failure, timeout,
nonzero exit, invalid UTF-8 or JSONL, or oversized stdout keeps the last valid
snapshot and shows a warning.
The default timeout is 10 seconds. Grafatui accepts at most 10 MiB of stdout and captures at most 64 KiB of stderr. A provider must finish its work before it exits: on Linux and macOS it runs in its own process group, and when the refresh ends (successfully, with an error or timeout, or because Grafatui quits) any processes it started are killed. On Windows only the provider process itself is stopped. Providers inherit Grafatui’s current directory and environment. Put credentials in that environment or use standard credential tooling; never place secrets in dashboard JSON or command arguments.
The included Git provider is a practical starting point:
cargo build --example git_annotation_provider
printf '%s\n' '{"version":1,"range":{"from":"2026-08-12T10:00:00Z","to":"2026-08-12T10:05:00Z"}}' \
| ./target/debug/examples/git_annotation_provider .
JSONL Event Format and Targeting
The file contains one JSON object per line. Blank lines are ignored. Each event
requires time to be an RFC3339 string with an explicit timezone or offset
(numeric timestamps are rejected) and non-empty text. tags is an optional
array of non-empty strings.
{"time":"2026-07-23T14:30:00Z","text":"Maintenance window","tags":["maintenance"]}
{"time":"2026-07-23T14:30:00Z","text":"Deployed v2.4","tags":["deploy","production"],"panel_titles":["HTTP Request Rate by Status Code"]}
Omit panel_titles to target all eligible graph and timeseries panels, as in
the first event. When panel_titles is present, it must contain one or more
non-blank titles and each title is matched exactly and case-sensitively against
eligible graph/timeseries panel titles. null, an empty array, and blank
titles are validation errors.
If a title occurs on multiple eligible panels, the event fans out to all of them and Grafatui shows one warning for that duplicate title. A title that is missing, or exists only on a non-graph panel, shows one warning and renders no marker for that title. These titles are Grafatui routing labels, not Grafana panel IDs.
Events are ordered by timestamp. Unknown JSON fields are ignored. Times with fractional seconds are accepted, and the full fractional timestamp is used when projecting an event onto the graph even when the space-limited inline timestamp display shows less precision.
Target, Filter, Inspect, and Reload
This walkthrough uses current UTC timestamps so both events fall in the visible
15-minute range. It uses only POSIX shell tools; no jq is required.
First, start the bundled Prometheus demo stack from the repository root:
cd examples/demo && docker-compose up -d && sleep 5 && cd ../..
Then create the annotation file and run Grafatui:
annotation_demo_file=/tmp/grafatui-annotations-demo.jsonl
annotation_demo_time="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
printf '{"time":"%s","text":"Maintenance window","tags":["maintenance"]}\n' \
"$annotation_demo_time" > "$annotation_demo_file"
printf '{"time":"%s","text":"API deployed","tags":["deploy","production"],"panel_titles":["HTTP Request Rate by Status Code"]}\n' \
"$annotation_demo_time" >> "$annotation_demo_file"
cargo run -- \
--grafana-json examples/dashboards/prometheus_demo.json \
--prometheus-url http://localhost:19090 \
--range 15m \
--annotations-file "$annotation_demo_file"
Maintenance window appears on every graph/timeseries panel. API deployed
appears only on HTTP Request Rate by Status Code. Press t, select deploy
with Space, and press Enter; only the targeted deployment remains. Press
v, move the cursor to the marker, and press Enter; the selected panel’s
cluster list and selected-event detail pane open.
While Grafatui is running, append an event in a second terminal:
annotation_demo_file=/tmp/grafatui-annotations-demo.jsonl
annotation_demo_time="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
printf '{"time":"%s","text":"Rollback started","tags":["rollback","production"],"panel_titles":["HTTP Request Rate by Status Code"]}\n' \
"$annotation_demo_time" >> "$annotation_demo_file"
The new event is loaded after the normal refresh; Grafatui does not need to
restart. With only deploy selected, Rollback started remains hidden. Press
t, then c, and press Enter to apply the cleared filter and reveal the
rollback marker. Alternatively, select rollback in the filter and apply it.
Tag Filter and Cluster Controls
Press t to open the global annotation tag filter. It is runtime-only: it is
not written to the JSONL source or configuration, and applies to every eligible
panel. Selected tags use exact, case-sensitive OR matching: an event remains
visible when it has any selected tag. With no selected tags, events with and
without tags remain visible. The catalogue keeps a selected tag with a zero
event count so it can be removed after a reload.
In the tag filter, use Up/Down or j/k to move, Space to toggle the
highlighted tag, c to clear the draft, Enter to apply it, or Esc to
cancel without changing the applied filter. In inspection mode, Enter opens
only the cluster actually rendered by the selected panel at the cursor. In the
cluster, use Up/Down or j/k to select an event, PgUp/PgDn to page,
and Enter or Esc to close. Mouse input is ignored while either annotation
modal is open.
Cluster contents are frozen when the cluster opens: a later reload can replace the live annotation snapshot without moving rows or changing the cluster’s event details.
Automatic Reload, Rendering, and Exports
Grafatui refreshes both source types during each normal refresh, including while markers are hidden. It checks a file source’s metadata and, when it changes, reads, parses, and validates the full candidate file before atomically replacing the snapshot. A zero-byte file is a valid update that clears all events. Command and Prometheus refreshes share the same time range, start together, and redraw together. Annotation loading is independent of Prometheus: annotation failures never fail startup or a Prometheus refresh.
Only graph and timeseries panels receive annotation markers. Press a to
toggle marker visibility. Events that project to the same terminal column are
shown as one counted marker: • for one event, decimal 2–9 for two through
nine events, and + for 10 or more. In inspection mode, moving the cursor onto
that column shows the cluster’s timestamp, text, and tags inline; multi-event
details report the exact cluster count.
Applied panel targeting and tag filtering affect the markers in SVG/PNG exports and changed-frame recordings. Active inline annotation details are exportable; the tag-filter and cluster modal chrome is not.
Errors and Last Valid Snapshot
If a file is missing, unreadable, or contains a malformed event, Grafatui keeps rendering the last valid snapshot and shows an annotation warning. Command provider failures follow the same rule. A bad update does not replace the previously loaded events, fail startup, or fail the Prometheus refresh.
CI/CD and Provider Integrations
CI workflow → durable deployment/release record → command provider query
→ normalized JSONL point events → Grafatui overlay
GitHub Actions is a useful concrete pattern: let a workflow record deployment, release, or workflow outcomes in an API, object store, database, or shared event log. A local provider receives Grafatui’s requested range and queries that system of record, then emits normalized JSONL point events. Useful tags include repository, workflow, environment, status, commit, and deployment.
Give the provider credentials through its environment or standard credential tooling, never dashboard JSON or command arguments. A shared JSONL file is a reasonable source only when the workflow and Grafatui genuinely share storage; do not commit an ever-growing event log to the application repository. Vendor-specific providers should normally live as user or community plugins. Built-in integrations remain demand-driven.
Current Limits and Roadmap
This feature supports one external file or command source, point events,
panel-title routing, and one global runtime-only tag filter. It has no stable
event IDs, no per-panel tag filters, no multiple sources, and no editing. It
does not add Grafana annotation-query/API compatibility. Range events, stable
event IDs, and per-panel tag filters remain deferred. --validate validates
Grafana dashboard imports only; it does not validate annotations.
Grafana Dashboard Import
Grafatui imports supported Grafana dashboard JSON files and renders supported panels in the terminal.
| Format | Status | Requirements |
|---|---|---|
| Classic JSON | ✅ Supported | Non-resource object with fields such as title, panels, and templating |
| V2 Resource JSON | 🔶 Partial | Exact apiVersion: dashboard.grafana.app/v2 and recursive grid, row, tab, or AutoGrid containers |
| V2 Resource YAML | 🔶 Partial | The same V2 subset, read from a .yaml or .yml file |
| V1 Resource JSON | ❌ Unsupported | The dashboard.grafana.app/v1 resource envelope is not accepted |
--grafana-json reads .yaml/.yml files as YAML. .json files and files
with any other extension are parsed strictly as JSON.
The supported V2 subset maps inline Panel elements, Prometheus PanelQuery
queries, top-level variables, timeSettings.autoRefresh, supported field
configuration, fixed-grid positions, AutoGrid placement, and nested RowsLayout/TabsLayout containers to the
same Grafatui behavior as Classic JSON.
Rows and tabs may recursively contain GridLayout, RowsLayout, TabsLayout,
or AutoGridLayout. AutoGrid adapts columns to terminal width and supports
fixed row heights, fillScreen growth, and table content fitting with minimum/maximum heights, row matching, and body scrolling. See Static AutoGrid sizing
for supported sizing options and the runnable example.
AutoGrid items also support repeats and variable/data/time-range conditions, with dashboard and containing row/tab variable scopes. See the dynamic example.
Grafana’s resource API writes empty lists and objects as null (for example
links, transformations, options, and variables), and its exporter may
omit them entirely. Grafatui treats both the same as an empty value, so
dashboards exported from Grafana 13 import unchanged. Exports made with
Share dashboard with another instance enabled also work: their queries carry
no datasource and run against the Prometheus server given by --prometheus-url.
Library panels are exported as a reference to the library panel’s uid, without the panel itself, so Grafatui skips them with an import diagnostic. Enable Share dashboard with another instance when exporting to inline library panels into the dashboard.
Repeated rows/tabs, row/tab conditions, and library panels remain unsupported; unsupported V2 layouts and fields are fatal import errors. Repeated grid items are also rejected rather than silently changing the dashboard.
Export From Grafana
Grafana 13 exports dashboards in the V2 Resource model by default:
- Open the dashboard in Grafana.
- In the toolbar, open Export and select Export as code.
- Choose JSON or YAML.
- Download the file, or copy it into a local
.jsonor.yamlfile. - Run Grafatui with
--grafana-json.
grafatui --prometheus-url http://localhost:9090 --grafana-json ./node-exporter.yaml
For dashboards that use V2 features Grafatui does not support yet, export the Classic model instead: under Export as code, expand Advanced options, set Model to Classic, and save the JSON. Grafana documents the available models and export controls in Export a dashboard as code.
Supported Panel Types
Grafatui currently supports:
graphtimeseriesstatgaugebargaugetableheatmap
Classic row headers and their collapsed state are rendered and interactive. Collapsed descendants are excluded from navigation, search, refresh, and export. Hidden-header rows are transparent: they consume no header and their children remain visible.
Variables
Grafatui reads dashboard variables from templating.list and expands $var and ${var} in PromQL expressions.
Defaults come from the dashboard JSON. Override them from the CLI:
grafatui --grafana-json ./dash.json --var job=node --var instance=server-01
Prometheus query variables such as label_values(up, instance) and query_result(...) are resolved before panel queries run.
Grafatui honors Classic refresh values 0, 1, and 2, and schema-v2
never, onDashboardLoad, and onTimeRangeChanged. Dashboard-load variables
resolve once; time-range variables also resolve after manual refresh, zoom, pan,
or reset. Periodic panel refreshes do not re-run already resolved variables.
Variables in a row or tab resolve once when that scope first becomes visible.
Dependent variables resolve in declaration order when an earlier value changes.
If a refresh fails, Grafatui keeps the previous selection, continues with other
due variables, and shows a compact footer warning. A later successful refresh
clears that warning. label_values(...) uses Prometheus’s bounded label-values
endpoint with the current start/end window and an optional metric selector.
Value Mappings
Grafatui imports exact numeric Grafana value mappings whose results contain
text. Mapped text is used by Stat, Gauge, BarGauge, Table, graph legends, and
SVG/PNG exports. The underlying numeric value remains unchanged for sorting,
scaling, thresholds, sparklines, and graph axes; an unmatched value continues
through the normal unit and decimal formatter.
Range, regex, null/NaN special-value mappings, and mapping colors are not part
of this focused subset. Unsupported mapping kinds produce an import warning;
null values continue to use the existing noValue behavior where supported.
Import Diagnostics
Grafatui prints import warnings before starting the TUI when a dashboard uses
important Grafana features that are skipped or ignored. Diagnostics include
unsupported panel types, unsupported value-mapping kinds, reduce options, unresolved variables,
unsupported V2 datasources, and unsupported variable modifiers such as
${var:regex}. V2 diagnostics retain their spec.* source paths.
Run a non-interactive check with:
grafatui --validate --grafana-json ./dash.json
Warnings do not make validation fail. A dashboard that can be parsed and imported exits successfully even if diagnostics are printed.
Use --strict to make warnings fail validation, or --format json to emit a
machine-readable summary. Fatal V2 layout and repeat errors fail validation in
all modes; --strict additionally fails when import diagnostics are present:
grafatui --validate --strict --grafana-json ./dash.json
grafatui --validate --format json --grafana-json ./dash.json
Hidden Targets
Grafatui honors targets[].hide by skipping hidden targets during import.
Panels with a mix of hidden and visible targets render only the visible target
queries.
Query Modes
Grafatui honors targets[].instant from Grafana dashboard JSON. Targets marked
as instant use the Prometheus instant query endpoint, while range targets use
query_range.
If a target does not specify instant, Gauge, Bar Gauge, and Table panels
default to instant queries. Graph, Timeseries, Stat, and Heatmap panels default
to range queries.
Field Configuration
Grafatui applies selected fieldConfig.defaults values where they map cleanly
to terminal rendering:
minandmaxset explicit Graph y-axis bounds and Gauge limits.thresholdsrender graph threshold lines and drive dynamic coloring for Stat, Gauge, and Bar Gauge panels.unit,decimals, andnoValueaffect supported panel values, axes, legends, and exports.custom.axisGridShowcontrols per-panel graph guide lines.
Built-In PromQL Variables
Grafatui expands the following Grafana-style variables:
$__interval$__interval_ms$__range$__range_s$__range_ms$__rate_interval$__rate_interval_ms
$__interval is the query’s step. Without an explicit step/--step, it
scales with the time range and honors panel maxDataPoints and interval and
target interval; an explicit step is used exactly. See query
resolution.
Compatibility Details
See the Grafana compatibility matrix for field-by-field support details.
Exporting and Recording
Grafatui can export the current dashboard view as SVG, PNG, or both. It can also record changed dashboard states into a timestamped frame bundle.
Export a Snapshot
Press e to export the current visible dashboard.
Output files are written under --export-dir:
grafatui --export-dir ./grafatui-exports --export-format both
Supported formats:
svgpngboth
Record Changed Frames
Press Ctrl+E to start recording. Press Ctrl+E again, or quit with q, to finalize the bundle.
Grafatui records only changed rendered states:
grafatui-recording-<timestamp>/
frame-000001.svg
frame-000002.svg
manifest.json
If --export-format png or both is selected, matching PNG files are written too.
Export and recording failures are shown in the TUI instead of ending the session. Files are replaced atomically, so a failed write does not leave a partially written export in place.
An active recording is finalized once when Grafatui quits normally, returns an application error, receives Ctrl-C, or receives SIGTERM/SIGHUP on Unix. The same shutdown path restores the cursor, leaves the alternate screen, disables mouse capture, and disables raw mode on normal returns and panic unwinding. Cleanup is best effort so one failing terminal operation does not skip the remaining ones.
When external annotations are visible, their panel targeting and applied tag filter affect the markers written to SVG/PNG exports and changed-frame recordings. Any active inline annotation details remain exportable. Annotation modal chrome is omitted from exports and recordings, and draft-only tag-filter edits do not create recording frames; a frame can change after the filter is applied or cleared.
Recording Limits
Limit the number of changed frames in one recording:
grafatui --record-max-frames 300
When the frame cap is reached, Grafatui stops writing new frames and records completed_reason = "capped" when finalized.
Manifest
Each recording writes a manifest.json file with metadata for downstream tooling:
{
"version": 1,
"format": "svg",
"changed_only": true,
"frame_count": 2,
"max_frames": 300,
"completed_reason": "stopped",
"viewport": { "width": 100, "height": 40 },
"frames": [
{
"index": 1,
"elapsed_ms": 0,
"files": ["frame-000001.svg"]
}
]
}
Keyboard and Mouse
Grafatui is designed for keyboard-first dashboard inspection.
Keyboard Controls
| Key | Action |
|---|---|
q | Quit |
r | Force refresh |
+ / - | Zoom out / in |
[ / ] | Pan left / right in time |
0 | Reset to live mode |
Up / Down or k / j | Select previous or next visible row or panel |
Enter / Space | Toggle the selected row |
Left / Right | Collapse / expand the selected row |
PgUp / PgDn | Scroll vertically, or select panels in fullscreen |
Home / End | Jump to top or bottom |
Ctrl+Up / Ctrl+Down | Scroll selected capped AutoGrid table/error body by one row |
Ctrl+PgUp / Ctrl+PgDn | Page that body by its visible capacity |
Ctrl+Home / Ctrl+End | First / last body window |
y | Toggle Y-axis mode |
g | Toggle autogrid guide lines |
a | Toggle external annotation markers |
t | Open the global annotation tag filter |
1 through 9 | Toggle series visibility |
f | Toggle fullscreen mode for the selected panel |
v | Toggle value inspection mode |
Enter in inspect mode | Open the selected panel’s annotation cluster at the cursor |
e | Export current view |
Ctrl+E | Start or stop changed-frame recording |
/ | Search visible rows and panels |
Left / Right | Move cursor in inspect mode |
? | Toggle debug info |
Mouse Support
| Action | Behavior |
|---|---|
| Click | Select a row or panel; click a row disclosure marker to toggle it; move the cursor in fullscreen inspect mode |
| Drag | Move the cursor in fullscreen inspect mode |
| Scroll | Scroll the selected overflowing capped table/error body under the pointer; otherwise scroll the dashboard |
| Shift+scroll | Scroll the dashboard vertically |
In normal mode, clicking selects rows or panels. Press v or f on a selected
panel to use cursor-focused interactions.
Annotation Modals
The global tag filter opens with t. Use Up/Down or k/j to move,
Space to toggle the highlighted tag, c to clear the draft, Enter to apply
it, or Esc to discard it. In an annotation cluster, use Up/Down or
k/j to select an event, PgUp/PgDn to page, and Enter or Esc to
close it. Mouse input is ignored while either annotation modal is open.
Body controls require a fitting Table in an AutoGrid group with a finite maximum.
They use exactly Ctrl, outside search and annotation modals; borders and table
headers stay fixed. The footer shows a hint when that body can scroll. Wheel
input over headers, borders, gaps or other panels scrolls the dashboard; wheel
input at an owned body’s boundary stays with that body. Unmodified navigation
keys keep their existing actions. Press f when a tiny cap leaves no visible
body; fullscreen bypasses the cap and retains these controls.
Examples
The repository includes example Grafana dashboards and a local demo environment.
Demo Stack
Start Prometheus, node-exporter, and mock vLLM metrics:
cd examples/demo
docker-compose up -d
Run Grafatui from the repository root:
cargo run -- --grafana-json examples/dashboards/prometheus_demo.json --prometheus-url http://localhost:19090
Stop the demo:
cd examples/demo
docker-compose down -v
Included Dashboards
-
examples/dashboards/prometheus_demo.json: recommended first demo for the bundled Prometheus stack. -
examples/dashboards/all_visualizations.json: compact dashboard showing the supported visualization types, including timeseries bars, area fill, point mode, and hidden-axis examples. -
examples/dashboards/instant_queries.json: demonstrates explicit instant targets and the default instant behavior for summary panels. -
examples/dashboards/thresholds_demo.json: demonstrates thresholds, field bounds, and threshold marker rendering. -
examples/dashboards/grafana_v2_compatibility.json: exact Grafana V2 resource with aGridLayout, a dynamic Prometheusjobvariable, refresh settings, and two supported panels. Run it with:cargo run -- --grafana-json examples/dashboards/grafana_v2_compatibility.json --prometheus-url http://localhost:19090 -
examples/dashboards/grafana_v2_rows.json: exact Grafana V2 resource showing expanded, nested, and initially collapsedRowsLayoutrows with three Prometheus panels. Run it with:cargo run -- --grafana-json examples/dashboards/grafana_v2_rows.json --prometheus-url http://localhost:19090 -
examples/dashboards/grafana_v2_tabs.json: exact Grafana V2 resource with tab, row, and empty-content behavior. Focus a tab bar and use Left/Right to switch or Enter/Space to enter its content. Run it with:cargo run -- --grafana-json examples/dashboards/grafana_v2_tabs.json --prometheus-url http://localhost:19090 -
examples/demo/vllm/grafana.json: vLLM-oriented dashboard for the mock demo services.
More Detail
See the repository example docs:
Grafana Dashboard JSON Compatibility
This document provides a comprehensive feature-parity table between the Grafana dashboard JSON models and what Grafatui currently supports.
Snapshot: Grafatui v0.1.13. The roadmap prioritizes Grafana parity first, then user-visible product value. See the roadmap for milestone slices built from this compatibility ladder.
Legend:
- ✅ Supported — Fully implemented and working
- 🔶 Partial — Partially implemented or with limitations
- ❌ Not Implemented — Recognized but not yet functional
- ⛔ Not Applicable — Cannot be implemented in a TUI context (e.g., browser-only features)
Dashboard Schema Models
Grafatui imports the non-resource Classic JSON model and recursive GridLayout,
RowsLayout, TabsLayout, and AutoGridLayout containers from the V2 Resource JSON model. In Grafana 13, use
Export as code → Advanced options → Model: Classic as the fallback for
unsupported advanced V2 dashboards. See the
dashboard import guide for detailed steps.
| Model | Status | Notes |
|---|---|---|
| Classic JSON | ✅ Supported | Accepted by --grafana-json; the remaining tables describe support for its fields |
| V1 Resource JSON | ❌ Not Implemented | The Kubernetes-style dashboard.grafana.app/v1 resource envelope is not accepted |
| V2 Resource JSON | 🔶 Partial | Exact dashboard.grafana.app/v2 resources with recursive grid, row, tab, and AutoGrid layouts are supported |
| V2 Resource YAML | 🔶 Partial | The same V2 subset, read from .yaml/.yml files |
V2 Resource Subset
| V2 field or behavior | Status | Notes |
|---|---|---|
Exact apiVersion: dashboard.grafana.app/v2 | ✅ Supported | Other resource versions are rejected |
| Grafana 13 Export as code and API output | ✅ Supported | Absent or null lists and objects (links, transformations, options, overrides, variables, …) are treated as empty, as Grafana’s API serializes them |
| Share dashboard with another instance exports | ✅ Supported | Queries without a datasource use the configured Prometheus; cleared query variable selections resolve dynamically |
spec.layout.kind: GridLayout | ✅ Supported | GridLayoutItem coordinates map to Grafatui’s fixed 24-column grid |
spec.layout.kind: RowsLayout | ✅ Supported | Nested grid, row, tab, and AutoGrid children preserve row titles, nesting, collapsed state, and hidden-header transparency |
spec.layout.kind: TabsLayout | ✅ Supported | Nested grid, row, tab, and AutoGrid children preserve titles and show one active tab per group |
data.spec.queryOptions interval, maxDataPoints | ✅ Supported | As for Classic panel interval and maxDataPoints; a query’s interval sets its min step |
Inline Panel elements | ✅ Supported | Supported panel visualization groups map through the Classic-equivalent importer |
Prometheus PanelQuery queries | ✅ Supported | prometheus, grafana-amazonprometheus-datasource, and grafana-azureprometheus-datasource query groups are imported; other datasources emit import diagnostics and are skipped |
RowsLayoutRow.spec.fillScreen | ⛔ Not Applicable | Accepted and ignored; terminal rows size to their content |
Top-level spec.variables | 🔶 Partial | Supported variable kinds map to Grafatui variables; unsupported kinds emit diagnostics |
spec.variables[].spec.refresh | ✅ Supported | never, onDashboardLoad, and onTimeRangeChanged control initial and manual/time-range resolution |
spec.timeSettings.autoRefresh | ✅ Supported | Used as the dashboard refresh interval |
vizConfig.spec.fieldConfig | 🔶 Partial | The supported Classic-equivalent field configuration subset applies |
AutoGridLayout | ✅ Supported | Ordered panels at root or inside rows/tabs; responsive columns, fillScreen, table content fitting, minimum/maximum heights and row matching share TUI, hit-test, scroll, and export geometry; repeats and conditional visibility use scoped variables |
| Repeated grid items and row repeat | ❌ Not Implemented | Rejected as fatal import errors |
| Row/tab local variables | 🔶 Partial | Supported variable kinds retain scopes for descendant panel queries, AutoGrid repeats and predicates; a newly visible scope resolves its variables once |
| AutoGrid item conditional rendering | ✅ Supported | Variable, query-data and time-range conditions; AND/OR and show/hide composition |
| Row/tab conditional rendering | ❌ Not Implemented | Deferred V2 feature |
LibraryPanel elements | ❌ Not Implemented | Exports reference library panels by uid only; they are skipped with a diagnostic. Export with Share dashboard with another instance enabled to inline them |
Static AutoGrid sizing
Stages 1–3 support AutoGridLayoutItem panel references in source order, including
empty groups, collapsed rows, and inactive tabs. Column counts respond to the
available terminal width. Resizing preserves panel selection and adjusts scroll
to keep it reachable. SVG/PNG snapshots and recordings use the same layout.
| AutoGrid setting | Supported behavior |
|---|---|
maxColumnCount | Default 3; finite values at least 1; fractions round down and emit autogrid_column_limit_rounded (rejected by --strict) |
columnWidthMode | narrow, standard, wide, or custom; default standard |
columnWidth | Positive finite pixels required for custom; rounds upward to cells |
rowHeightMode | short, standard, tall, or custom; default standard |
rowHeight | Positive finite pixels required for custom; rounds upward to cells |
matchRowHeights | Boolean; default true stretches siblings to their row track; false retains each panel contribution, leaving unused gaps |
fillScreen | Boolean; default false. True grows rows to fill their containing content viewport, preserving baseline minimum heights |
Layout/item fitContent | Boolean; layout default false, item omission inherits layout, explicit true/false overrides it. Tables fit visible rows or wrapped errors; other renderers retain baseline height |
minHeightMode, minHeight | Omission inherits row height; none removes the floor; short/standard/tall use 10/18/29 cells; custom requires positive finite pixels rounded upward |
maxHeightMode, maxHeight | Omitted/unlimited has no cap; short/standard/tall use 10/18/29 cells; custom requires positive finite pixels rounded upward. Only custom consumes maxHeight |
Item repeat, conditionalRendering | Selected-value repeats and scoped variable, data, and time-range conditions; detailed below |
| Unknown settings | Emit unsupported_autogrid_setting; --strict rejects the warning |
The logical scale is 10 pixels per column and 18 pixels per row, shared with
exports. Width presets become 20/45/77 columns; height presets become 10/18/29
rows. Panels use the widest fitting column count, capped by maxColumnCount
and item count, with at least one column. Remainder cells go to earlier columns.
These are terminal adaptations; panel dimensions do not scale with font size.
Run the static example with:
grafatui --grafana-json examples/dashboards/grafana_v2_autogrid.json \
--prometheus-url http://localhost:9090
With fillScreen: true, extra height is shared across row tracks, with remainder
cells assigned to earlier rows. Each visible row header or tab bar deducts one
cell from its child viewport; hidden row headers deduct nothing. Minimum row
heights can overflow and scroll. Scrolling and preceding siblings do not change
the allocation; following siblings begin after the filled grid’s full extent.
Empty grids consume no height. This setting does not enter single-panel fullscreen.
Run the fill-screen example with the same command, substituting its filename.
Table fitting counts only visible series, preserving existing ordering, value
formatting, threshold colors and single-row cell clipping. A nonempty table needs
N + 4 cells for N rows, its column header, margin and borders; an empty table
needs 3. Widths with no body space need only borders. Unsupported renderers
(graph, unknown, stat, gauge, bar gauge, heatmap), including their errors, retain the baseline. Table errors fit their wrapped body lines plus two border cells.
A missing minimum inherits rowHeight; supplied dimensions are validated even
when their mode does not consume them. Fixed panels still contribute their full
baseline to prevent overlap beside shorter fitted tables.
With fitting, row tracks follow the tallest contribution. fillScreen distributes
only extra height above those tracks and keeps a baseline track floor.
matchRowHeights: false keeps individual heights even in filled grids; unused
gaps have no click target. This setting now also prevents fixed panels from
stretching during fill growth. Dashboard scrolling reveals lower fitted table
rows while keeping the clipped panel title and table header visible. Refreshes
reconcile focus and scroll after table data grows or shrinks. SVG/PNG and
recordings use the same row window and logical 18-pixel spacing.
Run the content-fit example with the same command, substituting its filename.
Maximum heights bound fitting panels, including after row matching and fill
stretch a track. The minimum wins when it exceeds the maximum. Ordinary panels
keep their baseline sizing. To use a short cap below the standard row height,
set minHeightMode: none, as in the bounded example.
Capped fitting tables have independent body offsets. Ctrl+Up/Ctrl+Down
scroll one row, Ctrl+PgUp/Ctrl+PgDn page, and Ctrl+Home/Ctrl+End reach
the first/last window. Borders and table headers stay fixed; error bodies use
the same controls and wrapped lines in TUI, SVG/PNG and recordings. Only the
selected overflowing body owns unmodified wheel input; headers, borders, gaps,
other panels and Shift+wheel keep dashboard scrolling. Search and annotation
modals retain their controls.
Fullscreen bypasses the cap and uses its visible body capacity, including when a tiny cap cannot fit data. The same offset is clamped on entry/exit, resize, data or visibility changes. Changing errors and error recovery reset it; row collapse and inactive tabs retain offsets until reactivation. Ordinary and unlimited-fit fullscreen keep their existing presentation.
AutoGrid layout semantics are supported for Grafatui’s panel renderers and data sources. Selected variable lists retain source order and labels; All repeats concrete options instead of a query regex. Containing row/tab declarations shadow dashboard values, and each repeated panel has its own binding. Surviving instances retain data, focus and body scroll when selections change.
Item conditions support all four variable operators, query-data presence and range duration with AND/OR and show/hide composition. Pending results are omitted; a group without known results remains visible. Invalid regexes return true as in Grafana; invalid intervals remain pending. Month/year intervals use fixed 30/365 day durations; the upstream condition rejects milliseconds and truncates a decimal interval count before applying its unit. Hidden data-dependent instances continue refreshing while their containing row/tab is active. Variable/time-hidden items without a data predicate skip queries. Hidden items reserve no grid cells.
Run the dynamic example
against Prometheus to repeat up tables for the selected instances. Zooming beyond
one hour hides those tables through the range condition. Repeated rows/tabs and
their conditional settings, library panels and transformations remain separate
compatibility work.
Unsupported settings fail during import, including inside inactive tabs and collapsed rows.
V2 resources are accepted as JSON or YAML. Use a Classic export for any advanced V2 dashboard outside this grid, rows, tabs, and AutoGrid subset.
Dashboard-Level Properties
| JSON Field | Status | Notes |
|---|---|---|
title | ✅ Supported | Displayed in the title bar |
uid | ❌ Not Implemented | Not used (not needed for local JSON import) |
id | ❌ Not Implemented | Not used |
version | ❌ Not Implemented | Not used |
tags | ❌ Not Implemented | Ignored |
timezone | ❌ Not Implemented | All timestamps displayed in UTC |
editable | ⛔ Not Applicable | Grafatui is read-only |
style | ⛔ Not Applicable | TUI has its own theme system |
schemaVersion | ❌ Not Implemented | Not validated |
refresh | ✅ Supported | Used as the default data refresh interval; overridden by config or --refresh-rate |
time | ❌ Not Implemented | Uses --range CLI option instead |
time.from / time.to | ❌ Not Implemented | Uses --range CLI option instead |
fiscalYearStartMonth | ⛔ Not Applicable | |
liveNow | ❌ Not Implemented | Uses 0 key to reset to live instead |
weekStart | ⛔ Not Applicable |
Panels
Panel Types
| Panel Type | Status | Notes |
|---|---|---|
graph (legacy) | ✅ Supported | Rendered as a line chart (Braille markers) |
timeseries | ✅ Supported | Mapped to graph renderer |
stat | ✅ Supported | Big value + sparkline |
gauge | ✅ Supported | Horizontal gauge bar |
bargauge | ✅ Supported | Vertical bar chart |
table | ✅ Supported | Two-column table (Series, Value) |
heatmap | ✅ Supported | Character-based block heatmap |
row | ✅ Supported | Headers, nested rows, and collapsed state are rendered and interactive |
text | ❌ Not Implemented | Skipped during import |
dashlist | ❌ Not Implemented | Skipped during import |
alertlist | ❌ Not Implemented | Skipped during import |
news | ⛔ Not Applicable | |
annolist | ❌ Not Implemented | |
barchart | ❌ Not Implemented | Skipped (distinct from bargauge) |
candlestick | ❌ Not Implemented | |
canvas | ⛔ Not Applicable | Interactive canvas not feasible in TUI |
datagrid | ❌ Not Implemented | |
debug | ⛔ Not Applicable | |
geomap | ⛔ Not Applicable | Map visualization not feasible in TUI |
histogram | ❌ Not Implemented | |
logs | ❌ Not Implemented | |
nodeGraph | ⛔ Not Applicable | |
piechart | ❌ Not Implemented | |
state-timeline | ❌ Not Implemented | |
status-history | ❌ Not Implemented | |
trend | ❌ Not Implemented | |
xychart | ❌ Not Implemented |
Graph & Timeseries Parity
| Feature | JSON Field | Behavior | Grafana | Grafatui |
|---|---|---|---|---|
| Draw styles | fieldConfig.defaults.custom.drawStyle | Line, points, and bars map to terminal graph styles | 🟡 | ✅ |
| Point display | fieldConfig.defaults.custom.showPoints | always overlays visible point markers; never suppresses area/line point markers | 🟡 | 🔶 |
| Area fill | fieldConfig.defaults.custom.fillOpacity | Nonzero fill opacity renders terminal/SVG area fill behind the line | 🟡 | 🔶 |
| Stacking | fieldConfig.defaults.custom.stacking | Parsed and retained; non-off modes render non-stacked in this slice | 🟡 | 🔶 |
| Axis placement | fieldConfig.defaults.custom.axisPlacement | hidden suppresses y-axis labels; left/right map to the terminal y-axis | 🟡 | 🔶 |
| Axis grid | fieldConfig.defaults.custom.axisGridShow | Controls per-panel autogrid guide lines | 🟡 | ✅ |
| Threshold style | fieldConfig.defaults.custom.thresholdsStyle | Dashed/line style is parsed for graph threshold rendering | 🟡 | 🔶 |
Panel Common Fields
| JSON Field | Status | Notes |
|---|---|---|
title | ✅ Supported | Displayed as the panel border title |
type | ✅ Supported | Used to select the renderer |
gridPos | ✅ Supported | 24-column grid layout fully supported |
gridPos.x | ✅ Supported | |
gridPos.y | ✅ Supported | |
gridPos.w | ✅ Supported | |
gridPos.h | ✅ Supported | |
id | ❌ Not Implemented | Not used |
description | ❌ Not Implemented | Not displayed |
timeFrom / timeShift / hideTimeOverride | ❌ Not Implemented | Ignored without a diagnostic; panels use the dashboard range |
interval | ✅ Supported | Panel min interval, including > prefixes and variables; invalid values emit a diagnostic. See query resolution |
maxDataPoints | ✅ Supported | Sets how many steps a range query divides the time range into; defaults to 1000 |
transparent | ⛔ Not Applicable | TUI panels always have borders |
links | ⛔ Not Applicable | No browser navigation |
repeat | ❌ Not Implemented | Template repeat not supported |
repeatDirection | ❌ Not Implemented | |
maxPerRow | ❌ Not Implemented | |
collapsed (row) | ✅ Supported | Initial collapsed state is preserved and interactive |
panels (nested in row) | ✅ Supported | Nested panels and rows retain their hierarchy |
Targets (Queries)
| JSON Field | Status | Notes |
|---|---|---|
targets (array) | ✅ Supported | Multiple targets per panel supported |
targets[].expr | ✅ Supported | PromQL expression |
targets[].legendFormat | ✅ Supported | {{label}} syntax for legend formatting |
targets[].refId | ❌ Not Implemented | Not used |
targets[].datasource | ❌ Not Implemented | Only Prometheus datasource is supported |
targets[].interval | ✅ Supported | Per-query min step; overrides the panel interval |
targets[].intervalFactor | ❌ Not Implemented | |
targets[].instant | ✅ Supported | Uses Prometheus instant query when true; Gauge, BarGauge, and Table default to instant |
targets[].format | ❌ Not Implemented | Always treated as time_series |
targets[].hide | ✅ Supported | Hidden targets are skipped during import |
targets[].exemplar | ❌ Not Implemented | |
targets[].editorMode | ⛔ Not Applicable | UI-only setting |
PromQL Special Variables
| Variable | Status | Notes |
|---|---|---|
$__rate_interval | ✅ Supported | max($__interval + scrape interval, 4 × scrape interval), as in Grafana; the scrape interval is the query’s min interval or --scrape-interval (default 15s) |
$__rate_interval_ms | ✅ Supported | Millisecond form of $__rate_interval |
$__interval | ✅ Supported | The exact configured step/--step, or, when absent, the range divided by maxDataPoints (default 1000), rounded as Grafana does, no finer than the imported/default min interval, and within Prometheus’s 11,000-point limit |
$__interval_ms | ✅ Supported | Millisecond form of $__interval |
$__range | ✅ Supported | Current dashboard time range |
$__range_s | ✅ Supported | Current dashboard time range in seconds |
$__range_ms | ✅ Supported | Current dashboard time range in milliseconds |
Templating (Variables)
| JSON Field | Status | Notes |
|---|---|---|
templating.list | ✅ Supported | Variables extracted from dashboard |
templating.list[].name | ✅ Supported | Used as $var or ${var} in queries |
templating.list[].current.value | ✅ Supported | Used as default value |
templating.list[].current.text | 🔶 Partial | Used as fallback if value is missing |
templating.list[].allValue | ✅ Supported | Used when value is $__all, falls back to .* |
templating.list[].type | 🔶 Partial | query variables are resolved for Prometheus |
templating.list[].query | 🔶 Partial | Supports Prometheus label_values(...) and query_result(...) |
templating.list[].definition | 🔶 Partial | Used as a fallback query expression for dynamic query variables |
templating.list[].datasource | ❌ Not Implemented | |
templating.list[].regex | 🔶 Partial | Applied to dynamic query variable results |
templating.list[].sort | ❌ Not Implemented | |
templating.list[].multi | ❌ Not Implemented | Multi-value selection not supported |
templating.list[].includeAll | ❌ Not Implemented | |
templating.list[].refresh | ✅ Supported | 0 keeps a saved value, 1 resolves on load, and 2 also resolves on manual/time-range changes; periodic panel refreshes do not re-run variables |
templating.list[].options | ❌ Not Implemented | No dropdown/picker UI |
templating.list[].hide | ❌ Not Implemented | |
CLI --var KEY=VALUE override | ✅ Supported | Overrides dashboard defaults from command line |
Config file vars override | ✅ Supported | Overrides via TOML config |
Variable Substitution
| Pattern | Status | Notes |
|---|---|---|
$varname | ✅ Supported | Simple substitution |
${varname} | ✅ Supported | Braced substitution |
${varname:regex} | ❌ Not Implemented | Format modifiers not supported |
${varname:pipe} | ❌ Not Implemented | |
${varname:csv} | ❌ Not Implemented | |
${varname:json} | ❌ Not Implemented | |
${varname:queryparam} | ❌ Not Implemented | |
$__all | ✅ Supported | Replaced with allValue or .* |
Field Configuration (fieldConfig)
fieldConfig is partially implemented. Thresholds, explicit min/max bounds,
selected display formatting fields, threshold style, and per-panel autogrid
settings are parsed; exact numeric-to-text value mappings are supported, while
display names and field overrides remain major gaps.
| JSON Field | Status | Notes |
|---|---|---|
fieldConfig | 🔶 Partial | Parsed for supported defaults/custom fields below |
fieldConfig.defaults | 🔶 Partial | Parsed for min/max, thresholds, and selected custom fields |
fieldConfig.defaults.unit | 🔶 Partial | Common units such as bytes, bits, seconds, milliseconds, percent, percentunit, ops, request rate, and byte rate are formatted; unknown units fall back to Grafatui’s compact SI formatter |
fieldConfig.defaults.min | ✅ Supported | Used for Graph y-axis lower bounds, percentage thresholds, and Gauge limits |
fieldConfig.defaults.max | ✅ Supported | Used for Graph y-axis upper bounds, gauge scaling, and threshold boundaries |
fieldConfig.defaults.decimals | ✅ Supported | Controls numeric precision in panel values, graph axes, legends, and exports |
fieldConfig.defaults.color | ❌ Not Implemented | Uses theme palette instead |
fieldConfig.defaults.mappings | 🔶 Partial | Exact numeric value mappings render mapped text in Stat, Gauge, BarGauge, Table, graph legends, and exports; range, regex, special-value, and mapping-color behavior is not implemented, and unsupported mapping kinds produce import diagnostics |
fieldConfig.defaults.noValue | 🔶 Partial | Used for null Stat/Gauge/Table values and exports; empty panels still show Grafatui’s No data state |
fieldConfig.defaults.displayName | ❌ Not Implemented | |
fieldConfig.defaults.custom | 🔶 Partial | Used for graph draw style, fill/points, axis placement, stacking metadata, threshold style, and axis grid visibility |
fieldConfig.defaults.custom.lineWidth | ❌ Not Implemented | TUI limitation |
fieldConfig.defaults.custom.fillOpacity | 🔶 Partial | Nonzero values enable terminal/SVG area fill; exact browser opacity is approximated |
fieldConfig.defaults.custom.pointSize | ⛔ Not Applicable | TUI points use fixed terminal-cell markers |
fieldConfig.defaults.custom.axisLabel | ❌ Not Implemented | |
fieldConfig.defaults.custom.axisGridShow | ✅ Supported | Controls per-panel autogrid guide lines for graph/time-series panels |
fieldConfig.defaults.custom.thresholdsStyle | 🔶 Partial | mode is parsed for threshold rendering; glyph style is also controlled by Grafatui’s marker setting |
fieldConfig.defaults.custom.scaleDistribution | ❌ Not Implemented | Always linear |
fieldConfig.overrides | ❌ Not Implemented |
Thresholds
| JSON Field | Status | Notes |
|---|---|---|
fieldConfig.defaults.thresholds | ✅ Supported | Applied to Graph limit lines and dynamic coloring for Stat, Gauge & BarGauge |
fieldConfig.defaults.thresholds.mode | ✅ Supported | (absolute / percentage) |
fieldConfig.defaults.thresholds.steps | ✅ Supported | |
fieldConfig.defaults.thresholds.steps[].value | ✅ Supported | Evaluated mathematically against metric values |
fieldConfig.defaults.thresholds.steps[].color | ✅ Supported | Maps keywords (e.g., green) and hex codes (e.g., #FF0000) |
Panel Options (options)
Panel-specific options are not parsed yet. Grafatui currently applies its own
compact TUI defaults for legends, stat sparklines, gauges, and inspect-mode
tooltips.
| JSON Field | Status | Notes |
|---|---|---|
options | 🔶 Partial | Graph presentation and Stat/Gauge/BarGauge reduction subsets are imported |
options.legend | ❌ Not Implemented | Grafatui uses its own compact legend |
options.legend.displayMode | ❌ Not Implemented | Always shows inline legend |
options.legend.placement | ❌ Not Implemented | Always bottom |
options.legend.calcs | ❌ Not Implemented | No calculated legend values (min/max/avg) |
options.tooltip | ❌ Not Implemented | Inspect mode serves as tooltip substitute |
options.tooltip.mode | ❌ Not Implemented | |
options.orientation | ❌ Not Implemented | |
options.reduceOptions | 🔶 Partial | Classic and V2 Stat/Gauge/BarGauge support one calculation per series over raw range samples; an explicit target instant setting still wins. Unsupported settings warn and retain default selection. |
options.reduceOptions.calcs | 🔶 Partial | last, lastNotNull, min, max, mean, and sum (Total); empty list defaults to last. Multiple or unknown calculations are unsupported. |
options.reduceOptions.fields | 🔶 Partial | Only omitted or empty selection; nonempty field filters and values: true are unsupported. limit has no effect when values is false. |
options.textMode | ❌ Not Implemented | |
options.colorMode | ❌ Not Implemented | |
options.graphMode | ❌ Not Implemented | Stat always shows sparkline |
Annotations
| JSON Field | Status | Notes |
|---|---|---|
annotations | ❌ Not Implemented | External file/command providers do not implement this Grafana field. |
annotations.list | ❌ Not Implemented | External file/command providers do not implement this Grafana field. |
Grafatui external file/command JSONL events are a separate, opt-in read-only source.
panel_titles is Grafatui’s external-source routing field: it matches eligible
graph/timeseries panel titles exactly, not Grafana panel IDs. It does not imply
compatibility with Grafana annotation queries, APIs, annotations, or
annotations.list.
Data Links & Transformations
| JSON Field | Status | Notes |
|---|---|---|
options.dataLinks | ⛔ Not Applicable | No browser navigation in TUI |
transformations | ❌ Not Implemented | |
transformations[].id | ❌ Not Implemented | (e.g., organize, merge, reduce) |
Alert Rules
| JSON Field | Status | Notes |
|---|---|---|
alert | ❌ Not Implemented | Panel-level alerts |
alert.conditions | ❌ Not Implemented | |
alert.notifications | ❌ Not Implemented |
Datasource Configuration
| Feature | Status | Notes |
|---|---|---|
Prometheus (query_range) | ✅ Supported | Primary and only supported datasource |
Prometheus (query instant) | ✅ Supported | Used for dynamic template variables and instant panel targets |
| Prometheus labels API | ✅ Supported | Dynamic label_values(...) requests include the active start/end window and an optional match[] selector |
| Mixed datasource | ❌ Not Implemented | |
| InfluxDB | ❌ Not Implemented | |
| Loki | ❌ Not Implemented | |
| Elasticsearch | ❌ Not Implemented | |
| Other datasources | ❌ Not Implemented |
Summary Statistics
| Category | Supported | Partial | Not Implemented | Not Applicable |
|---|---|---|---|---|
| Dashboard Properties | 1 | 0 | 10 | 4 |
| Panel Types | 8 | 0 | 14 | 5 |
| Panel Common Fields | 11 | 0 | 5 | 2 |
| Targets / Queries | 4 | 0 | 7 | 1 |
| PromQL Variables | 7 | 0 | 0 | 0 |
| Templating | 6 | 6 | 6 | 0 |
| Variable Substitution | 3 | 0 | 5 | 0 |
| Field Config | 4 | 7 | 9 | 2 |
| Thresholds | 5 | 0 | 0 | 0 |
| Panel Options | 0 | 0 | 14 | 0 |
| Annotations | 0 | 0 | 2 | 0 |
| Data Links / Transforms | 0 | 0 | 2 | 1 |
| Alert Rules | 0 | 0 | 3 | 0 |
| Datasources | 3 | 0 | 5 | 0 |
| Total | 52 | 13 | 82 | 15 |
Most Requested Missing Features
Based on user feedback, the following missing features are most commonly expected:
- Broader unit formatting (
fieldConfig.defaults.unit) — Extend the current common-unit subset to more Grafana unit families - Broader reduce options (
options.reduceOptions) — Add field filters, all-values mode, and multiple calculations beyond the supported single-calculation subset - Broader value mappings (
fieldConfig.defaults.mappings) — Add range and useful special-value mappings beyond exact numeric labels - Import diagnostics — Warn clearly about skipped panel types and ignored high-impact fields
- Additional panel types —
text,piechart,histogram,logs
What Grafatui Does Instead
Grafatui provides several TUI-native capabilities that don’t map directly to Grafana JSON features:
| Grafatui Feature | Description |
|---|---|
| 8 color themes | default, dracula, monokai, solarized-dark, solarized-light, gruvbox, tokyo-night, catppuccin |
| Keyboard navigation | Vim-style (j/k), arrow keys, page up/down |
| Panel search | / opens a fuzzy-search popup |
| Fullscreen mode | f to focus on a single panel |
| Inspect mode | v enables cursor-based point-in-time inspection |
| Y-axis toggle | y switches between auto-scale and zero-based |
| Series toggling | 1–9 to show/hide individual series |
| Autogrid toggle | g toggles automatic guide lines |
| Mouse support | Click to select, scroll to navigate, drag cursor in fullscreen |
| Smart caching | Request deduplication and caching for identical queries |
| Client-side downsampling | Max-pooling to ~200 points to preserve peaks |
| SVG/PNG export and recordings | Save dashboard snapshots or changed-frame recording bundles |
| TOML configuration | Persistent config file for all CLI options |
This document was reviewed against the Grafatui source code on 2026-10-13. If you notice any inaccuracies, please open an issue or PR.
Troubleshooting
Prometheus Connection Refused
Check that Prometheus is running and reachable:
curl http://localhost:9090/-/healthy
If you are using the demo stack, the Prometheus port is 19090:
curl http://localhost:19090/-/healthy
TLS Configuration Errors
Grafatui validates configured certificate files at startup. If startup fails:
- use an
https://Prometheus URL whenever a TLS option is set; - set
client_certandclient_keytogether; - verify that the configured paths are readable PEM files and that the client private key is unencrypted;
- verify that
client_keybelongs to the first certificate inclient_cert.
For a private-CA server, inspect the server chain and the configured CA bundle:
openssl s_client -connect prometheus.internal:9090 -showcerts </dev/null
openssl x509 -in ca.pem -noout -subject -issuer -enddate
For mutual TLS, verify the client certificate and key match:
openssl x509 -in client.pem -pubkey -noout | openssl pkey -pubin -outform DER | openssl sha256
openssl pkey -in client.key -pubout -outform DER | openssl sha256
Grafatui reports file paths and validation failures, but never certificate or private-key contents.
No Data Appears
Prometheus may need a few scrape intervals before data is available. Wait 10 to 15 seconds and force a refresh with r.
Also confirm that the dashboard queries match labels in your Prometheus server:
grafatui --prometheus-url http://localhost:9090 --grafana-json ./dashboard.json --var job=prometheus
Dashboard Variables Do Not Match
Override variables explicitly with --var:
grafatui --grafana-json ./dashboard.json --var instance=localhost:9090
If a Grafana dashboard uses multi-select formatting modifiers such as ${var:csv} or ${var:regex}, check the compatibility matrix. Not every Grafana interpolation mode is implemented.
If a Prometheus-backed variable fails to refresh, Grafatui keeps its previous
selection and shows Variable NAME failed: ... in the footer. Other due
variables and panels continue refreshing. Force a retry with r; a successful
retry clears the warning.
Stale Data and Query Warnings
Grafatui keeps the last successful series for a query when a later refresh
fails. The panel remains readable and its title shows stale when every
displayed query is stale, or partial when fresh and stale query results are
mixed. Select the panel to see the query error in the footer.
A warning marker means Prometheus returned data with a warning or info
message. A later successful refresh clears both the marker and its detail.
These markers are also included in SVG and PNG export titles so snapshots do not present stale or incomplete data as fully current.
Demo Port Conflict
The demo Prometheus service uses host port 19090. If that port is already in use, edit examples/demo/docker-compose.yml and run Grafatui with the updated URL.
Export Directory Problems
Set an explicit export directory:
grafatui --export-dir ./grafatui-exports
Make sure the directory is writable by your current user.