Documents ZigTSDB v1.5.0 · applies to the 1.5.x line
CLI Reference
zigtsdb-cli is the command-line client for a running Anzaran server:
querying, ingestion, monitoring, and cluster operations.
Installation
The CLI ships inside the Docker image at /app/zigtsdb-cli — no separate install
step. On a running deployment, invoke it with:
$ docker exec -it anzaran /app/zigtsdb-cli <command>
Replace anzaran with your container name. A native binary download is planned.
Synopsis
zigtsdb-cli <command> [args] [options]
Flags after the command name belong to the command (e.g.
cluster preflight --peers ...); unknown flags before a command are an error.
Authorization header on data commands.
Against a server with JWT auth enabled (TSDB_AUTH_SECRET set), gated commands
return Error: HTTP 401. The exceptions are backup and
license activate, which read an admin token from TSDB_AUTH_TOKEN
when auth is on. The CLI speaks plain HTTP and presents no client certificate — behind the
secure overlay, use curl or a client library against a node's sidecar.
Global Options
| Option | Short | Description | Default |
|---|---|---|---|
--endpoint <url> |
-e |
Server endpoint URL | http://localhost:8080 |
--format <fmt> |
-f |
Output format: table, json, csv |
table |
--verbose |
-V |
Verbose output | off |
--help |
-h |
Show help | — |
--version |
-v |
Show version | — |
query
Execute a ZQL query via GET /query.
$ zigtsdb-cli query "SELECT avg(value) FROM cpu"
$ zigtsdb-cli query "SELECT avg(value) FROM cpu WHERE time > now() - 5m"
$ zigtsdb-cli query "SELECT avg(value) FROM t GROUP BY time_bucket(time, 1h)"
$ zigtsdb-cli query "SELECT rate(value) FROM requests WHERE host =~ 'web-.*'"
$ zigtsdb-cli query "SELECT percentile(value, 95) FROM latency GROUP BY region"
$ zigtsdb-cli query "SELECT count(value) FROM events" --format json
$ zigtsdb-cli query "SELECT max(value) FROM metrics" -e http://prod:8080
ingest
Ingest line protocol from a file or stdin (-). --batch posts to
/ingest/batch (JSON batch body) instead of /ingest. Limits: 10 MB
from stdin, 100 MB from file.
$ zigtsdb-cli ingest data.lp
$ cat data.lp | zigtsdb-cli ingest -
$ echo "cpu,host=web-1 value=42.5 1704067200000000000" | zigtsdb-cli ingest -
Line protocol format (up to 16 labels per line, timestamps in Unix nanoseconds):
<metric>[,<label>=<value>...] value=<float> <timestamp_ns>
metrics, stats, usage, prometheus
| Command | Description |
|---|---|
metrics |
List metric names (GET /metrics) |
stats |
Database statistics (GET /stats): points, metrics, cache entries |
usage |
Usage statistics (GET /usage) |
prometheus |
Print the server's Prometheus exposition (GET /metrics/prometheus) |
raw-points, verify
raw-points [--metric <m>] [--limit <n>] fetches raw points
(GET /raw_points). This is a scoped internal endpoint on the server
(block-transfer auth); on a server with internal auth configured — or fail-closed
unconfigured — it returns 401. Also accepted as raw_points.
verify runs a data-integrity check (GET /verify): local vs S3 series
counts.
compact
compact triggers compaction (POST /compact);
compact --status reads its status (GET /compact/status).
scheduler
$ zigtsdb-cli scheduler status
$ zigtsdb-cli scheduler run compaction
status— background-job summary (GET /scheduler/status).run <job>— force-run a job. Jobs:compaction,purge,index_flush,tombstone_gc,retention_purge,orphan_block_gc.
scheduler enable / disable send
POST /scheduler/enable without a job name, but the server only routes per-job
paths, so these two subcommands currently get 404. Use
curl -X POST "$EP/scheduler/enable/<job>?enabled=false" instead.
retention, delete
retention --days <N> [--metric <m>] sends
POST /retention. The server treats this as an immediate purge and ignores the
body — so this command triggers a purge; it does not set a policy. Set
retention policies with PUT /retention on the HTTP API.
delete --metric <name> [--before <timestamp>] deletes a metric
(DELETE /delete?metric=...). The server deletes all data for the
metric; --before is transmitted but not evaluated server-side.
backup
$ zigtsdb-cli backup create
$ zigtsdb-cli backup list
$ zigtsdb-cli backup prune --keep 3
$ zigtsdb-cli backup verify /data/backups/<id>
$ zigtsdb-cli backup restore /data/backups/<id> --into /data \
[--data-dir <dir>] [--force] [--skip-raft]
create(POST /backup),list(GET /backup) andprune --keep N(keeps the N newest;--keep 0is refused) talk to the server; they need an admin token viaTSDB_AUTH_TOKENwhen auth is on.verifyandrestoreare offline:verifyrecomputes every CRC inMANIFEST.jsonand exits 1 naming the first bad or missing file;restoreverifies first, refuses a non-empty target unless--force, copies every file back and fsyncs;--skip-raftomits Raft state so the node rejoins a live cluster.
A backup is the node's own point-in-time state (sealed files, WAL tail, Raft state,
metadata) under <TSDB_WAL_DIR>/backups/<id>/; S3 blocks are not
included. It lives on the same volume, so copy it off-node if it must survive the host.
license, profile
$ zigtsdb-cli license
$ zigtsdb-cli license activate --key-file /run/secrets/anzaran_license
$ cat key.txt | zigtsdb-cli license check -
license— the node's license status (GET /license: tier, licensed_to, expiry, meter).license activate <key> | --key-file <path> | -— installs a key (POST /license/activate; admin token viaTSDB_AUTH_TOKENwhen auth is on) and reports whether it was persisted.license check <key> | --key-file <path> | -— validates a key without installing it (GET /license?key=).
Prefer --key-file or - (stdin) over a key on the command line, which is
visible in ps and shell history. Keys and tiers:
License & tiers.
profile "<ZQL>" sends GET /profile?zql=.... The server does not
route /profile, so this returns 404.
cluster
HA operations against a Raft cluster.
| Subcommand | Description |
|---|---|
cluster status |
GET /cluster/status — cluster health, node list, versions, shard
groups |
cluster flush |
POST /flush — flush the memtable to S3 |
cluster sync |
POST /sync — replay this node's WAL segments from S3 |
cluster rebalance |
POST /rebalance — report the current shard assignment |
cluster add-node / remove-node
Raft membership changes. Require TSDB_RAFT_ADMIN_TOKEN in the environment
(deliberately not a CLI flag — argv leaks via ps and shell history). If the
request hits a follower, the CLI follows the 503 leader_url hint and retries once
against the leader; any further non-200 is surfaced.
$ TSDB_RAFT_ADMIN_TOKEN=... zigtsdb-cli cluster add-node http://node3:8080
$ TSDB_RAFT_ADMIN_TOKEN=... zigtsdb-cli cluster remove-node 3
cluster preflight
Decide whether a rolling upgrade to this CLI binary's version is safe. The CLI's compiled-in protocol window is compared against every node's advertised window; any FAIL line makes the command exit non-zero, and the upgrade must not proceed.
$ TSDB_RAFT_ADMIN_TOKEN=... zigtsdb-cli cluster preflight \
--peers http://node0:8080,http://node1:8080,http://node2:8080 \
[--max-apply-lag N] [--skip-migration-check]
Peers come from --peers, else TSDB_PEERS, else the configured
endpoint. Checks: node health, protocol-window overlap, routing-epoch agreement with no staged
entries, topology-epoch agreement, no active data migration (needs
TSDB_RAFT_ADMIN_TOKEN; bypass only with --skip-migration-check),
per-node apply lag within --max-apply-lag (default 0), and uniform secure-mode
posture.
preflight: candidate protocol 1 (min-compatible 1), 3 peer(s)
PASS: http://node0:8080 healthy
PASS: http://node0:8080 protocol window [1,1] compatible
PASS: http://node0:8080 routing epoch 4 agrees
PASS: no active migration
PASS: http://node0:8080 apply lag 0 <= 0
...
preflight: PASS — safe to roll this binary
Output Formats
table(default) — human-readable.json— raw server JSON, for scripting (... --format json | jq).csv— comma-separated, for spreadsheet import.
Exit Codes
| Code | Meaning |
|---|---|
0 |
Success |
2 |
Invalid arguments |
| non-zero | Failed command (e.g. HTTP error, unreachable server, preflight FAIL) |
Examples
# Watch a live aggregate
$ watch -n 1 'zigtsdb-cli query "SELECT avg(value) FROM cpu WHERE time > now() - 1m"'
# Hourly rollup to CSV
$ zigtsdb-cli query \
"SELECT avg(value) FROM cpu GROUP BY time_bucket(time, 1h)" \
--format csv > hourly_cpu.csv
# Cluster health gate for scripts
$ zigtsdb-cli cluster status --format json | jq -e '.status == "healthy"'
# Bulk ingest
$ for f in data/*.lp; do zigtsdb-cli ingest "$f"; done
See also
- ZQL Reference — query syntax
- Quickstart — run a server in 5 minutes
- Documentation overview — every page for this release line