Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

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

KeyAction
qQuit
rForce refresh
+ / -Zoom out / in
[ / ]Pan left / right
fFullscreen selected panel
Enter / Space on a rowToggle the row
Left / Right on a rowCollapse / expand the row
vInspect 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

OptionDescriptionDefault
--prometheus-url <URL>Prometheus server URLhttp://localhost:9090
--ca-cert <FILE>PEM CA certificates to trust in addition to the built-in public rootsnone
--client-cert <FILE>PEM client certificate chain for mutual TLS; requires --client-key or its config equivalentnone
--client-key <FILE>PEM private key for the mutual TLS client certificatenone
--grafana-json <FILE>Grafana dashboard file: Classic JSON, or V2 resource JSON or YAMLnone
--annotations-file <FILE>Read-only external JSONL point-event filenone
--annotations-command <PROGRAM>Read-only executable annotation providernone
--annotations-command-arg <ARG>Argument for --annotations-command; repeat to preserve ordernone
--annotations-command-timeout <DURATION>Maximum command-provider runtime10s
--validateCheck the Grafana dashboard import and exit without starting the TUIfalse
--strictMake --validate fail when diagnostics contain warningsfalse
--format <FORMAT>Output format for --validate: text or jsontext
--range <DURATION>Time range window, such as 5m, 1h, or 24h5m
--step <DURATION>Exact query step, such as 5s or 30s; omit it for automatic range-aware resolutionautomatic, minimum 5s
--scrape-interval <DURATION>Prometheus scrape interval, used for $__rate_interval15s
--var <KEY=VALUE>Override a dashboard variablenone
--theme <NAME>UI themedefault
--threshold-marker <MARKER>Marker for threshold linesdashed
--autogrid-color <COLOR>Color for automatic graph grid lines and labelsdark-gray
--export-dir <DIR>Directory for exports and recordings./grafatui-exports
--export-format <FORMAT>svg, png, or bothsvg
--record-max-frames <COUNT>Maximum changed frames per recording300
--refresh-rate <MS>Data fetch interval in milliseconds1000
--config <FILE>Configuration file pathnone

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_cert may contain multiple PEM CA certificates. They augment the built-in public roots rather than replacing them.
  • client_cert contains the client certificate followed by any intermediate certificates. client_key accepts 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:

  1. Divide the time range by the panel’s maxDataPoints, or by 1000 when it is unset.
  2. Round to a Grafana interval, such as 20s, 1m, or 10m.
  3. Use the min interval instead when it is larger. The min interval is the target’s interval, then the panel’s interval, then 5s.
  4. 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:

  • default
  • dracula
  • monokai
  • solarized-dark
  • solarized-light
  • gruvbox
  • tokyo-night
  • catppuccin

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.

FormatStatusRequirements
Classic JSON✅ SupportedNon-resource object with fields such as title, panels, and templating
V2 Resource JSON🔶 PartialExact apiVersion: dashboard.grafana.app/v2 and recursive grid, row, tab, or AutoGrid containers
V2 Resource YAML🔶 PartialThe same V2 subset, read from a .yaml or .yml file
V1 Resource JSON❌ UnsupportedThe 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:

  1. Open the dashboard in Grafana.
  2. In the toolbar, open Export and select Export as code.
  3. Choose JSON or YAML.
  4. Download the file, or copy it into a local .json or .yaml file.
  5. 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:

  • graph
  • timeseries
  • stat
  • gauge
  • bargauge
  • table
  • heatmap

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:

  • min and max set explicit Graph y-axis bounds and Gauge limits.
  • thresholds render graph threshold lines and drive dynamic coloring for Stat, Gauge, and Bar Gauge panels.
  • unit, decimals, and noValue affect supported panel values, axes, legends, and exports.
  • custom.axisGridShow controls 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:

  • svg
  • png
  • both

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

KeyAction
qQuit
rForce refresh
+ / -Zoom out / in
[ / ]Pan left / right in time
0Reset to live mode
Up / Down or k / jSelect previous or next visible row or panel
Enter / SpaceToggle the selected row
Left / RightCollapse / expand the selected row
PgUp / PgDnScroll vertically, or select panels in fullscreen
Home / EndJump to top or bottom
Ctrl+Up / Ctrl+DownScroll selected capped AutoGrid table/error body by one row
Ctrl+PgUp / Ctrl+PgDnPage that body by its visible capacity
Ctrl+Home / Ctrl+EndFirst / last body window
yToggle Y-axis mode
gToggle autogrid guide lines
aToggle external annotation markers
tOpen the global annotation tag filter
1 through 9Toggle series visibility
fToggle fullscreen mode for the selected panel
vToggle value inspection mode
Enter in inspect modeOpen the selected panel’s annotation cluster at the cursor
eExport current view
Ctrl+EStart or stop changed-frame recording
/Search visible rows and panels
Left / RightMove cursor in inspect mode
?Toggle debug info

Mouse Support

ActionBehavior
ClickSelect a row or panel; click a row disclosure marker to toggle it; move the cursor in fullscreen inspect mode
DragMove the cursor in fullscreen inspect mode
ScrollScroll the selected overflowing capped table/error body under the pointer; otherwise scroll the dashboard
Shift+scrollScroll 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 a GridLayout, a dynamic Prometheus job variable, 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 collapsed RowsLayout rows 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.

ModelStatusNotes
Classic JSON✅ SupportedAccepted by --grafana-json; the remaining tables describe support for its fields
V1 Resource JSON❌ Not ImplementedThe Kubernetes-style dashboard.grafana.app/v1 resource envelope is not accepted
V2 Resource JSON🔶 PartialExact dashboard.grafana.app/v2 resources with recursive grid, row, tab, and AutoGrid layouts are supported
V2 Resource YAML🔶 PartialThe same V2 subset, read from .yaml/.yml files

V2 Resource Subset

V2 field or behaviorStatusNotes
Exact apiVersion: dashboard.grafana.app/v2✅ SupportedOther resource versions are rejected
Grafana 13 Export as code and API output✅ SupportedAbsent 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✅ SupportedQueries without a datasource use the configured Prometheus; cleared query variable selections resolve dynamically
spec.layout.kind: GridLayout✅ SupportedGridLayoutItem coordinates map to Grafatui’s fixed 24-column grid
spec.layout.kind: RowsLayout✅ SupportedNested grid, row, tab, and AutoGrid children preserve row titles, nesting, collapsed state, and hidden-header transparency
spec.layout.kind: TabsLayout✅ SupportedNested grid, row, tab, and AutoGrid children preserve titles and show one active tab per group
data.spec.queryOptions interval, maxDataPoints✅ SupportedAs for Classic panel interval and maxDataPoints; a query’s interval sets its min step
Inline Panel elements✅ SupportedSupported panel visualization groups map through the Classic-equivalent importer
Prometheus PanelQuery queries✅ Supportedprometheus, grafana-amazonprometheus-datasource, and grafana-azureprometheus-datasource query groups are imported; other datasources emit import diagnostics and are skipped
RowsLayoutRow.spec.fillScreen⛔ Not ApplicableAccepted and ignored; terminal rows size to their content
Top-level spec.variables🔶 PartialSupported variable kinds map to Grafatui variables; unsupported kinds emit diagnostics
spec.variables[].spec.refresh✅ Supportednever, onDashboardLoad, and onTimeRangeChanged control initial and manual/time-range resolution
spec.timeSettings.autoRefresh✅ SupportedUsed as the dashboard refresh interval
vizConfig.spec.fieldConfig🔶 PartialThe supported Classic-equivalent field configuration subset applies
AutoGridLayout✅ SupportedOrdered 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 ImplementedRejected as fatal import errors
Row/tab local variables🔶 PartialSupported variable kinds retain scopes for descendant panel queries, AutoGrid repeats and predicates; a newly visible scope resolves its variables once
AutoGrid item conditional rendering✅ SupportedVariable, query-data and time-range conditions; AND/OR and show/hide composition
Row/tab conditional rendering❌ Not ImplementedDeferred V2 feature
LibraryPanel elements❌ Not ImplementedExports 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 settingSupported behavior
maxColumnCountDefault 3; finite values at least 1; fractions round down and emit autogrid_column_limit_rounded (rejected by --strict)
columnWidthModenarrow, standard, wide, or custom; default standard
columnWidthPositive finite pixels required for custom; rounds upward to cells
rowHeightModeshort, standard, tall, or custom; default standard
rowHeightPositive finite pixels required for custom; rounds upward to cells
matchRowHeightsBoolean; default true stretches siblings to their row track; false retains each panel contribution, leaving unused gaps
fillScreenBoolean; default false. True grows rows to fill their containing content viewport, preserving baseline minimum heights
Layout/item fitContentBoolean; 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, minHeightOmission inherits row height; none removes the floor; short/standard/tall use 10/18/29 cells; custom requires positive finite pixels rounded upward
maxHeightMode, maxHeightOmitted/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, conditionalRenderingSelected-value repeats and scoped variable, data, and time-range conditions; detailed below
Unknown settingsEmit 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 FieldStatusNotes
title✅ SupportedDisplayed in the title bar
uid❌ Not ImplementedNot used (not needed for local JSON import)
id❌ Not ImplementedNot used
version❌ Not ImplementedNot used
tags❌ Not ImplementedIgnored
timezone❌ Not ImplementedAll timestamps displayed in UTC
editable⛔ Not ApplicableGrafatui is read-only
style⛔ Not ApplicableTUI has its own theme system
schemaVersion❌ Not ImplementedNot validated
refresh✅ SupportedUsed as the default data refresh interval; overridden by config or --refresh-rate
time❌ Not ImplementedUses --range CLI option instead
time.from / time.to❌ Not ImplementedUses --range CLI option instead
fiscalYearStartMonth⛔ Not Applicable
liveNow❌ Not ImplementedUses 0 key to reset to live instead
weekStart⛔ Not Applicable

Panels

Panel Types

Panel TypeStatusNotes
graph (legacy)✅ SupportedRendered as a line chart (Braille markers)
timeseries✅ SupportedMapped to graph renderer
stat✅ SupportedBig value + sparkline
gauge✅ SupportedHorizontal gauge bar
bargauge✅ SupportedVertical bar chart
table✅ SupportedTwo-column table (Series, Value)
heatmap✅ SupportedCharacter-based block heatmap
row✅ SupportedHeaders, nested rows, and collapsed state are rendered and interactive
text❌ Not ImplementedSkipped during import
dashlist❌ Not ImplementedSkipped during import
alertlist❌ Not ImplementedSkipped during import
news⛔ Not Applicable
annolist❌ Not Implemented
barchart❌ Not ImplementedSkipped (distinct from bargauge)
candlestick❌ Not Implemented
canvas⛔ Not ApplicableInteractive canvas not feasible in TUI
datagrid❌ Not Implemented
debug⛔ Not Applicable
geomap⛔ Not ApplicableMap 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

FeatureJSON FieldBehaviorGrafanaGrafatui
Draw stylesfieldConfig.defaults.custom.drawStyleLine, points, and bars map to terminal graph styles🟡✅
Point displayfieldConfig.defaults.custom.showPointsalways overlays visible point markers; never suppresses area/line point markers🟡🔶
Area fillfieldConfig.defaults.custom.fillOpacityNonzero fill opacity renders terminal/SVG area fill behind the line🟡🔶
StackingfieldConfig.defaults.custom.stackingParsed and retained; non-off modes render non-stacked in this slice🟡🔶
Axis placementfieldConfig.defaults.custom.axisPlacementhidden suppresses y-axis labels; left/right map to the terminal y-axis🟡🔶
Axis gridfieldConfig.defaults.custom.axisGridShowControls per-panel autogrid guide lines🟡✅
Threshold stylefieldConfig.defaults.custom.thresholdsStyleDashed/line style is parsed for graph threshold rendering🟡🔶

Panel Common Fields

JSON FieldStatusNotes
title✅ SupportedDisplayed as the panel border title
type✅ SupportedUsed to select the renderer
gridPos✅ Supported24-column grid layout fully supported
gridPos.x✅ Supported
gridPos.y✅ Supported
gridPos.w✅ Supported
gridPos.h✅ Supported
id❌ Not ImplementedNot used
description❌ Not ImplementedNot displayed
timeFrom / timeShift / hideTimeOverride❌ Not ImplementedIgnored without a diagnostic; panels use the dashboard range
interval✅ SupportedPanel min interval, including > prefixes and variables; invalid values emit a diagnostic. See query resolution
maxDataPoints✅ SupportedSets how many steps a range query divides the time range into; defaults to 1000
transparent⛔ Not ApplicableTUI panels always have borders
links⛔ Not ApplicableNo browser navigation
repeat❌ Not ImplementedTemplate repeat not supported
repeatDirection❌ Not Implemented
maxPerRow❌ Not Implemented
collapsed (row)✅ SupportedInitial collapsed state is preserved and interactive
panels (nested in row)✅ SupportedNested panels and rows retain their hierarchy

Targets (Queries)

JSON FieldStatusNotes
targets (array)✅ SupportedMultiple targets per panel supported
targets[].expr✅ SupportedPromQL expression
targets[].legendFormat✅ Supported{{label}} syntax for legend formatting
targets[].refId❌ Not ImplementedNot used
targets[].datasource❌ Not ImplementedOnly Prometheus datasource is supported
targets[].interval✅ SupportedPer-query min step; overrides the panel interval
targets[].intervalFactor❌ Not Implemented
targets[].instant✅ SupportedUses Prometheus instant query when true; Gauge, BarGauge, and Table default to instant
targets[].format❌ Not ImplementedAlways treated as time_series
targets[].hide✅ SupportedHidden targets are skipped during import
targets[].exemplar❌ Not Implemented
targets[].editorMode⛔ Not ApplicableUI-only setting

PromQL Special Variables

VariableStatusNotes
$__rate_interval✅ Supportedmax($__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✅ SupportedMillisecond form of $__rate_interval
$__interval✅ SupportedThe 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✅ SupportedMillisecond form of $__interval
$__range✅ SupportedCurrent dashboard time range
$__range_s✅ SupportedCurrent dashboard time range in seconds
$__range_ms✅ SupportedCurrent dashboard time range in milliseconds

Templating (Variables)

JSON FieldStatusNotes
templating.list✅ SupportedVariables extracted from dashboard
templating.list[].name✅ SupportedUsed as $var or ${var} in queries
templating.list[].current.value✅ SupportedUsed as default value
templating.list[].current.text🔶 PartialUsed as fallback if value is missing
templating.list[].allValue✅ SupportedUsed when value is $__all, falls back to .*
templating.list[].type🔶 Partialquery variables are resolved for Prometheus
templating.list[].query🔶 PartialSupports Prometheus label_values(...) and query_result(...)
templating.list[].definition🔶 PartialUsed as a fallback query expression for dynamic query variables
templating.list[].datasource❌ Not Implemented
templating.list[].regex🔶 PartialApplied to dynamic query variable results
templating.list[].sort❌ Not Implemented
templating.list[].multi❌ Not ImplementedMulti-value selection not supported
templating.list[].includeAll❌ Not Implemented
templating.list[].refresh✅ Supported0 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 ImplementedNo dropdown/picker UI
templating.list[].hide❌ Not Implemented
CLI --var KEY=VALUE override✅ SupportedOverrides dashboard defaults from command line
Config file vars override✅ SupportedOverrides via TOML config

Variable Substitution

PatternStatusNotes
$varname✅ SupportedSimple substitution
${varname}✅ SupportedBraced substitution
${varname:regex}❌ Not ImplementedFormat modifiers not supported
${varname:pipe}❌ Not Implemented
${varname:csv}❌ Not Implemented
${varname:json}❌ Not Implemented
${varname:queryparam}❌ Not Implemented
$__all✅ SupportedReplaced 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 FieldStatusNotes
fieldConfig🔶 PartialParsed for supported defaults/custom fields below
fieldConfig.defaults🔶 PartialParsed for min/max, thresholds, and selected custom fields
fieldConfig.defaults.unit🔶 PartialCommon 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✅ SupportedUsed for Graph y-axis lower bounds, percentage thresholds, and Gauge limits
fieldConfig.defaults.max✅ SupportedUsed for Graph y-axis upper bounds, gauge scaling, and threshold boundaries
fieldConfig.defaults.decimals✅ SupportedControls numeric precision in panel values, graph axes, legends, and exports
fieldConfig.defaults.color❌ Not ImplementedUses theme palette instead
fieldConfig.defaults.mappings🔶 PartialExact 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🔶 PartialUsed 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🔶 PartialUsed for graph draw style, fill/points, axis placement, stacking metadata, threshold style, and axis grid visibility
fieldConfig.defaults.custom.lineWidth❌ Not ImplementedTUI limitation
fieldConfig.defaults.custom.fillOpacity🔶 PartialNonzero values enable terminal/SVG area fill; exact browser opacity is approximated
fieldConfig.defaults.custom.pointSize⛔ Not ApplicableTUI points use fixed terminal-cell markers
fieldConfig.defaults.custom.axisLabel❌ Not Implemented
fieldConfig.defaults.custom.axisGridShow✅ SupportedControls per-panel autogrid guide lines for graph/time-series panels
fieldConfig.defaults.custom.thresholdsStyle🔶 Partialmode is parsed for threshold rendering; glyph style is also controlled by Grafatui’s marker setting
fieldConfig.defaults.custom.scaleDistribution❌ Not ImplementedAlways linear
fieldConfig.overrides❌ Not Implemented

Thresholds

JSON FieldStatusNotes
fieldConfig.defaults.thresholds✅ SupportedApplied 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✅ SupportedEvaluated mathematically against metric values
fieldConfig.defaults.thresholds.steps[].color✅ SupportedMaps 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 FieldStatusNotes
options🔶 PartialGraph presentation and Stat/Gauge/BarGauge reduction subsets are imported
options.legend❌ Not ImplementedGrafatui uses its own compact legend
options.legend.displayMode❌ Not ImplementedAlways shows inline legend
options.legend.placement❌ Not ImplementedAlways bottom
options.legend.calcs❌ Not ImplementedNo calculated legend values (min/max/avg)
options.tooltip❌ Not ImplementedInspect mode serves as tooltip substitute
options.tooltip.mode❌ Not Implemented
options.orientation❌ Not Implemented
options.reduceOptions🔶 PartialClassic 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🔶 Partiallast, lastNotNull, min, max, mean, and sum (Total); empty list defaults to last. Multiple or unknown calculations are unsupported.
options.reduceOptions.fields🔶 PartialOnly 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 ImplementedStat always shows sparkline

Annotations

JSON FieldStatusNotes
annotations❌ Not ImplementedExternal file/command providers do not implement this Grafana field.
annotations.list❌ Not ImplementedExternal 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.


JSON FieldStatusNotes
options.dataLinks⛔ Not ApplicableNo browser navigation in TUI
transformations❌ Not Implemented
transformations[].id❌ Not Implemented(e.g., organize, merge, reduce)

Alert Rules

JSON FieldStatusNotes
alert❌ Not ImplementedPanel-level alerts
alert.conditions❌ Not Implemented
alert.notifications❌ Not Implemented

Datasource Configuration

FeatureStatusNotes
Prometheus (query_range)✅ SupportedPrimary and only supported datasource
Prometheus (query instant)✅ SupportedUsed for dynamic template variables and instant panel targets
Prometheus labels API✅ SupportedDynamic 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

CategorySupportedPartialNot ImplementedNot Applicable
Dashboard Properties10104
Panel Types80145
Panel Common Fields11052
Targets / Queries4071
PromQL Variables7000
Templating6660
Variable Substitution3050
Field Config4792
Thresholds5000
Panel Options00140
Annotations0020
Data Links / Transforms0021
Alert Rules0030
Datasources3050
Total52138215

Most Requested Missing Features

Based on user feedback, the following missing features are most commonly expected:

  1. Broader unit formatting (fieldConfig.defaults.unit) — Extend the current common-unit subset to more Grafana unit families
  2. Broader reduce options (options.reduceOptions) — Add field filters, all-values mode, and multiple calculations beyond the supported single-calculation subset
  3. Broader value mappings (fieldConfig.defaults.mappings) — Add range and useful special-value mappings beyond exact numeric labels
  4. Import diagnostics — Warn clearly about skipped panel types and ignored high-impact fields
  5. 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 FeatureDescription
8 color themesdefault, dracula, monokai, solarized-dark, solarized-light, gruvbox, tokyo-night, catppuccin
Keyboard navigationVim-style (j/k), arrow keys, page up/down
Panel search/ opens a fuzzy-search popup
Fullscreen modef to focus on a single panel
Inspect modev enables cursor-based point-in-time inspection
Y-axis toggley switches between auto-scale and zero-based
Series toggling1–9 to show/hide individual series
Autogrid toggleg toggles automatic guide lines
Mouse supportClick to select, scroll to navigate, drag cursor in fullscreen
Smart cachingRequest deduplication and caching for identical queries
Client-side downsamplingMax-pooling to ~200 points to preserve peaks
SVG/PNG export and recordingsSave dashboard snapshots or changed-frame recording bundles
TOML configurationPersistent 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_cert and client_key together;
  • verify that the configured paths are readable PEM files and that the client private key is unencrypted;
  • verify that client_key belongs to the first certificate in client_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.