Running InvariantDB in production.
Deploy, monitor, back up, respond. The customer-facing shape of self-hosting. Detailed procedures ship with your production contract; this page is the surface you can read on the way to evaluating whether that fits.
You're the operator.
You run the binary on your own hardware or your own cloud. Everything on this page applies. Deployment, monitoring, backups, incident response — you own it, we support it.
Start with Deployment and Monitoring.
We're the operator.
Point-and-click provisioning, patching, backups, and failover live in the managed dashboard. Read this page to understand what we do on your behalf, or if you're evaluating whether self-host is a better fit for your regulatory posture.
Managed users start at /cloud/me/dashboard.
Contents
01 — Deployment
InvariantDB ships as a single statically-linked binary. A production node is the binary plus a systemd unit plus a data directory. There is no external database, no coordinator, no sidecar. The same binary serves reads, writes, backups, and metrics.
Systemd unit template
A working starting point. Adjust the User, WorkingDirectory, and Environment lines to match your host.
Environment variables you should know
These are the operator-facing knobs. Everything else has a safe default that we tune on your behalf as part of the release notes. If you need to reach past this list, you're inside the operator handbook territory — talk to support.
| Variable | Default | What it does |
|---|---|---|
GQ_GRAPH_DIR |
required | Absolute path to the graph data directory. Persistent state lives here. Back this up. |
GQ_HTTP_ADDR |
127.0.0.1 |
Address the HTTP listener binds to. Set to 0.0.0.0 behind a load balancer; leave loopback for single-host reverse-proxy setups. |
GQ_HTTP_PORT |
3000 |
Public HTTP port. Serves queries, writes, MCP, and /healthz + /metrics. |
GQ_ADMIN_PORT |
3199 |
Localhost-only admin listener. Bound to 127.0.0.1 unconditionally. Force-compaction, budget updates, key rotation live here. |
GQ_REQUIRE_AUTH |
0 |
When 1, the public HTTP port refuses unauthenticated requests. Recommended for every production deploy. Loopback admin port stays open regardless. |
GQ_MAX_OPEN_ENGINES |
16 |
Cap on how many graphs are held resident at once. Graphs above the cap are evicted LRU when idle. Raise this if your working set is larger than 16 graphs; the memory ceiling scales roughly linearly. |
GQ_ENGINE_PIN_LIST |
empty | Comma-separated list of graph names that stay resident regardless of eviction pressure. Warmed at startup so the first production request doesn't pay a first-touch latency cost. |
GQ_METRICS_CARDINALITY_CAP |
10000 |
Ceiling on unique label combinations emitted at /metrics. Prevents a runaway tenant from blowing up your scrape budget. Raise if you have very many small graphs; a metric-truncation event is surfaced when the cap bites. |
02 — Monitoring and alerting
Liveness
GET /healthz is the single source of truth. It returns HTTP 200 with a JSON body reporting engine state, replication topology (if enabled), and the embedder readiness block. Load balancers key on the status code; on-call keys on the body.
Metrics
GET /metrics exposes a Prometheus surface. Query rates, mutation rates, per-graph memory budgets, per-graph query budgets. Standard alerting stacks light up without custom integration.
Prometheus scrape config
Counter families to alert on
gq_writes_total
Committed mutations, labeled by graph. Rate drop to zero on a graph you expect traffic on is a load or auth incident.
gq_cypher_total
Cypher queries served, labeled by graph and outcome. Watch the error-outcome rate for regressions after a deploy.
gq_engine_per_graph_resident_bytes
Working-set bytes per graph. Alert on graphs approaching their configured memory budget.
Alert: rate exceeds 90% of budget for 5m.gq_id_map_delta_layers_bytes
Bytes held in the id-map delta chain. Steady growth without compaction indicates a stalled promote task — see the incident playbook.
gq_wal_replay_flushes_total
Fires once per startup replay. Rate above zero in steady state is unexpected and worth investigating.
gq_backup_last_success_timestamp
Unix timestamp of the last successful backup. Alert when age exceeds your backup RPO.
Alert: age > 24h.gq_integrity_refuse_total, gq_loopback_probe_failures_total, or gq_durability_drift_total is a page-immediately signal. These fire only when the engine has detected something it does not want to serve past.
03 — Backup and restore
Backup runs online. The writer stays available; a checkpoint captures a consistent view; a streaming archive is emitted while ordinary reads continue against the checkpointed state. Every archive ships with a cryptographic lineage manifest. Restore is offline and refuses to advance the graph if any file has drifted.
You need a scheduled backup on every production node. Ship archives off-host. Verify integrity on a schedule — the restore path re-hashes every file against the manifest, so a scheduled restore-into-a-scratch-graph is the honest end-to-end check.
Full guarantees, restore semantics, and the shape of the archive: backup + restore.
04 — Common operations
Every routine task exposes a REST endpoint on the admin port. All of the endpoints below are idempotent and safe to retry.
Rotate an API key
Create a new key, deploy it, revoke the old one. Rotation is a two-step so callers can update credentials before the old key stops working.
Create or drop a graph
Drop is irreversible — on-disk state is deleted. Take a backup first if you might want the data back.
Force a fresh checkpoint
The engine checkpoints automatically on a background sweep. Force one when you need a fresh point-in-time before a backup or immediately after a bulk load. Safe to call on a busy graph; overlapping requests coalesce.
Reclaim identity-map memory
Bulk-load workloads can grow the identity-map faster than the background task drains it. This endpoint promotes the accumulated deltas immediately, which frees memory and lowers open-time latency on the next restart.
Rebuild derived state
Derived indexes and connectivity metadata are rebuilt from canonical sources. Safe to run against a live graph. Use it after suspected on-disk drift, or as part of a support-directed recovery.
05 — Incident playbook
Five symptoms cover the overwhelming majority of production incidents. Each maps to a likely cause and a first-response action. The operator handbook shipped with your production contract expands every one of these into a full decision tree.
First request after startup is slow.
GQ_ENGINE_PIN_LIST. Startup warms every pinned graph before the accept queue opens, so the first request finds the graph already resident. First-touch latency is otherwise proportional to graph size.
Memory keeps climbing.
GQ_MAX_OPEN_ENGINES graphs resident; graphs above the cap are evicted LRU when idle, but a busy fleet may hold them all.
gq_engine_per_graph_resident_bytes{graph} for the outliers and either lower GQ_MAX_OPEN_ENGINES, tune per-graph budgets, or size the host up. Set a hard memory budget with POST /admin/graphs/<name>/budget.
Startup takes longer than usual.
gq_wal_replay_flushes_total tick. If startup exceeds your budget, force a compaction after startup and schedule regular compactions to keep replay bounded.
Query returns an integrity refuse error.
Vector index query returns a typed error.
CALL db.dropVectorIndex(label, prop) followed by CALL db.buildVectorIndex(label, prop). The dim-lock guarantees this class of mismatch surfaces as a typed error rather than silent corruption. Model selection is governed at the graph level, not per index.
06 — Escalation and support
When to file a ticket
File immediately on any of: integrity refuse, durability drift, loopback probe failures, or any metric family that starts emitting values you don't recognize. File within the business day for: memory or latency regressions, backup verification failures, unexpected metric-truncation events.
The observability surface is designed so the same information you page on is what we need to triage — there is rarely a hidden log we need to ask you to fetch. Structured events at /healthz and counters at /metrics are the escalation package.
What to include
- Graph name and tenant identifier
- Timestamp window (UTC)
- The full
/healthzJSON body at the time of the incident - Relevant counter deltas from
/metricsacross the window - Any error subcodes returned to callers
Support email
Response targets
P0: 30 min. P1: 4 hours. P2: 1 business day. Contractual SLA figures are set in the production agreement.
Handbook
Ships with your production contract.
Talk to us before you scale.
Every production customer gets a shared runbook, a paging channel, and a scheduled monthly review of counter trends. Set that up before your first launch, not during your first incident.