KDB knowledge

The kdb program

What the kdb program is, how it finds your databases, and what its exit codes mean.

kdb is the one program you need. It runs SQL, the interactive shell, the server, backups and repairs. It works in PowerShell, CMD, bash and zsh, on Windows, Linux and macOS.

A database is a folder on the disk. Many databases can live in one data folder (one sub-folder per database). Most commands take a <db> argument: the name of a database in the data folder, or the path of a database folder.

CommandWhat it doesHelp
kdb sqlRuns SQL once and prints the resultkdb sql
kdb shellA prompt where you type SQLkdb shell
kdb serveThe server: web console, HTTP API, PostgreSQL portkdb serve
kdb info, kdb check, kdb repair, kdb dump-fileLook at a database, find and fix damagemaintenance
kdb backup, kdb backups, kdb verify-backup, kdb restoreBackupsbackup and safety
kdb user ...Users of the serverkdb user
kdb version, kdb help [<topic>]Version; the list of commands and of every help topic, one topic, or a tutorial chapter, in the terminalbelow
kdb upgradeMake an older database ready for this KDB (backup, feature list, check)versions and upgrades
kdb featuresThe stored-format features a database uses now, and what uses themgoing back
kdb downgradeLet an older KDB open the data again, when it uses nothing newer (backup, feature list, check)going back
kdb skill, kdb skill installPrint, or install for Claude Code, the file that teaches an AI assistant how to use KDBthe Claude skill

kdb help

Syntax:

kdb
kdb help
kdb --help
kdb -h
kdb help <topic>
kdb help tutorial [<chapter>]

Prints the version and a short list of every command and option, then the list of help topics - the same pages a running server shows at /help - with their titles. kdb help <topic> prints one topic in the terminal (the Markdown source in a source checkout, the built page as text in a package); a word that is not a topic lists the topics whose name contains it. kdb help tutorial lists the tutorial chapters and kdb help tutorial 17 prints one (the tutorial is part of the source checkout only).

Command line: The list of commands

kdb help

Output:

kdb 0.2.0-dev

Usage:
  kdb info    <db>                               show files, versions and log position
  kdb check   <db> [--no-audit-chain]            read every block and log record, report damage;
                                                 then check the hash chain of _audit_log
  kdb repair  <db> [--backups <dir>] [--accept-log-loss]
                                                 fix damaged files from old versions or backups
  kdb backup  <db> <backups-dir> [--hard-links] [--keep-daily N] [--keep-weekly N]
  kdb backups <backups-dir>                      list backups, newest first
  kdb verify-backup <backup-dir>
  kdb restore <backup-dir> <new-data-dir> [--logs-from <old-data-dir>] [--until "YYYY-MM-DD HH:MM[:SS]"]
  kdb dump-file <file.kdb>                       header and block checksums of one file
  kdb serve   [<db>] [--db <name>] [--port 9900] [--bind 127.0.0.1] [--backups <dir>] [--token <t>] [--memory <MB>]
              [--sync-commits always|batch] [--threads N]  HTTP API, line protocol (POST /write) and web console
              [--pg-port 8832] [--pg-timestamptz] PostgreSQL protocol for Postgres drivers and tools (0 = off)
                                                 (Ctrl+C stops). Without <db>: every database in the data folder
  kdb promote [--url http://localhost:9900] [--token <t>]  make the standby at --url a primary: it stops
                                                 copying and takes changes (when the primary is lost)
  kdb shell   [<db>] [--backups <dir>] [--memory <MB>]  interactive SQL prompt (end statements with ;)
  kdb sql     <db> "SQL" [--format table|csv|json] [--max-rows N]
                                                 run SQL and print results (use - to read SQL from stdin)
  kdb user add <name> [--role admin|ddl|write|read]  add a server user; the password is asked for (or read
                                                 from a pipe), never given on the command line
  kdb user passwd <name>                         set a new password
  kdb user role <name> admin|ddl|write|read|none change the role
  kdb user list                                  users and roles (never passwords)
  kdb user remove <name>
  kdb user reset-2fa <name>                      turn two-factor login off (a lost phone)
                                                 (kdb user works on the data folder while kdb serve is stopped;
                                                 while it runs, use CREATE USER ... in its console)
  kdb version [<db>]                             this build and the stored-format features it reads;
                                                 with <db>: the features that database uses
  kdb upgrade <db> [--backups <dir>]             verified backup, then save the database's feature list, then check
  kdb features <db> | --data-dir <folder>        the stored-format features each database uses now, and what uses them
  kdb downgrade <db> | --data-dir <folder> --to <version> [--backups <dir>] [--no-backup]
                                                 let an older KDB open the data (--to 2026-09-13, or --known
                                                 <names> from the older kdb version); refused, naming what uses
                                                 each newer feature, when the data needs one
  kdb key new                                    print a new random master key (64 hex characters) for
                                                 encrypted databases: keep it in KDB_MASTER_KEY or a key file
                                                 (KDB_MASTER_KEY_FILE) outside the data folder
  kdb key rotate <folder>                        re-wrap the data key of an encrypted database folder, or of
                                                 every backup in a folder of backups, with KDB_MASTER_KEY
                                                 (the old key in KDB_MASTER_KEY_PREVIOUS); no data file changes
  kdb help [<topic> | tutorial [<chapter>]]     the help topics (and, in a source checkout, the tutorial)
                                                 in the terminal; without a topic: the list of topics
  kdb skill [install] [--dir <d>] [--url <u>] [--token <t>] [--home <checkout>]
                                                 print, or install into ~/.claude/skills/kdb, the file that
                                                 teaches an AI assistant in another project how to use KDB

<db>: a database name in the data folder (market), or the path of a database folder (D:\kdb\market, ./market).
Data folder: --data-dir <folder>, else kdb.conf, else the KDB_DATA_DIR environment variable,
  else "data" next to kdb.
--config <file>: kdb.conf, the JSON file the console's Settings page and ALTER SYSTEM write
  (default: kdb.conf next to kdb). A flag on the command line always wins over it, and it wins over
  the environment. It may hold: data_dir, wal_dir, wal_archive_mb, cache_mb, keep_versions, trash_keep_hours,
  trash_max_mb, slow_query_ms, http_threads, max_connections, lock_timeout_ms,
  idle_in_transaction_timeout_ms, rate_limit_ip, rate_limit_user, timer_workers, timezone,
  http_functions, http_timeout_ms, ilp_auto_create, step_up_actions, step_up_minutes, alert_webhook,
  tls_cert, tls_key, the backup_*
  settings, and "system" (what ALTER SYSTEM SET saved; SELECT pg_reload_conf() puts it in use).
  A database folder served by its path (kdb serve D:\kdb\market) reads only the three folders below.
  web_dir, help_dir and skill_dir name the folders of the console, the help and the Claude skill
  (else KDB_WEB_DIR, KDB_HELP_DIR, KDB_SKILL_DIR, else web/, help/ and skill/ next to kdb).
--memory <MB>: memory for table data kept loaded (default 512). More is faster on big tables.
--memory-limit <size>: one limit for everything the server holds (table data, unsaved changes,
  indexes, results, caches, what the allocator keeps), like 3GB or 3072 (MB). Near it, memory is
  given back (the result cache too); at it, quick statements still run in a small reserve and
  others get 'memory full' (HTTP 503, PostgreSQL 53200); over it, the running statement that
  holds the most is stopped with 'memory full'. Default: 85% of a container's
  memory limit, else of the machine's memory (env KDB_MEMORY_LIMIT; 0 or off = no limit).
--threads <N>: threads for big queries and writes (default and most: half of the CPU threads).
--cache-mb <MB>: memory for results of repeated queries, all databases together (default 256,
  0 = off; else kdb.conf cache_mb, else env KDB_CACHE_MB). A result is used again only while no
  table it read has changed; a quick one (under 1 ms) is kept when asked twice. Queries with
  random() are never kept; with now() only for a caller that takes an older answer
  (/exec?cache_now_ms=500, SET result_cache_now_ms = 500). One result may use a quarter of it,
  a pinned one three quarters. ALTER DATABASE db SET RESULT_CACHE = '256MB' gives a database
  a space of its own.
--pg-compat on|off: PostgreSQL 14's answers for every database without its own ALTER DATABASE ...
  SET pg_compat (default off; env KDB_PG_COMPAT): 7 / 2 = 3, 1.005 is an exact NUMERIC, a window
  with ORDER BY counts ties together, text sorts like en_US. A session can choose with
  SET kdb.pg_compat = on | off.
--pg-int-division on|off: on the Postgres port, whole number / whole number gives a whole
  number (7 / 2 = 3) as in PostgreSQL (default off; env KDB_PG_INT_DIVISION). A session can
  still choose with SET int_division = integer | float.
--reader-yield on|off: readers do not wait for a long trigger or CALL; they read the last
  committed rows (default on; env KDB_READER_YIELD). Off: readers wait for the writer's statement.
--per-table-reads on|off: a reader waits only for writers of the tables it reads, and not while
  a checkpoint writes its files (default on; env KDB_PER_TABLE_READS). Off: it waits for every writer.
--pin-threads auto|on|off: auto (default): on Linux, inside a CPU quota (docker --cpus, cgroup cpu.max)
  smaller than the CPUs the process may run on, every thread is pinned to the quota's worth of CPUs,
  one per core, and moved back when the quota goes (docker update --cpus); 32 connections on a 2-CPU
  quota of a 32-CPU host no longer run on all 32 and stall when the time is spent. on: each HTTP
  worker, PostgreSQL connection and query thread runs on one CPU of its own, taken in turn from as
  many CPUs as KDB may use, out of the cpuset; background threads stay on those CPUs. off: nothing is
  pinned (env KDB_PIN_THREADS). Ignored on Windows.
--read-only-calls on|off: a CALL of a procedure that changes nothing runs like a SELECT: no table
  locks, no transaction (default on; env KDB_READ_ONLY_CALLS).
--scoped-ddl on|off: CREATE/ALTER/DROP TABLE, INDEX and TRIGGER, TRUNCATE, CREATE TABLE AS and
  materialized views lock only the tables they touch; views, procedures and functions lock none
  (default on; env KDB_SCOPED_DDL). Off: every schema change locks every table.
--ddl-beside-writers on|off: with --scoped-ddl, CREATE TABLE, ALTER TABLE, RENAME TABLE and CREATE/DROP
  INDEX run beside a writer of other tables that is inside one long statement (default on; env
  KDB_DDL_BESIDE_WRITERS). Off: they wait until that writer pauses between two statements.
--log-lock-waits on|off: a statement that waits for a table lock longer than --deadlock-timeout
  writes one line to the log naming itself, the lock and who holds it (default off; env
  KDB_LOG_LOCK_WAITS; a session: SET log_lock_waits = on).
--deadlock-timeout 1s: when --log-lock-waits writes its line (default 1s; env KDB_DEADLOCK_TIMEOUT;
  a session: SET deadlock_timeout). Deadlocks are found at once whatever it is.
--skip-locked-beside on|off: SELECT ... FOR UPDATE SKIP LOCKED inside BEGIN reads beside other open
  transactions that hold the table and returns the rows they did not touch (default off; env
  KDB_SKIP_LOCKED_BESIDE). Its UPDATE of such a row waits for their COMMIT. Off: no rows there.
--table-writers one|many: many: several users change one table at the same time when each of their
  statements on it touches single rows by the primary key (one-row INSERT, UPDATE or DELETE ... WHERE
  id = x, a CALL or trigger of such statements); the same row is waited for, as in PostgreSQL. Anything
  else on the table waits for the whole table, as before (default one; env KDB_TABLE_WRITERS).
--snapshot-reads on|off: a reader of a table that a long CALL, trigger job or bulk write is changing
  does not wait: it reads a snapshot of the committed rows (default off; env KDB_SNAPSHOT_READS).
  Tables with an index too, while a writer is busy on them, at any size: the read uses the index
  on the snapshot (an idle BEGIN is taken out and the index used, as before).
--tail-files on|off: a checkpoint saves rows appended after the end of a partition in a small tail file
  instead of writing the whole partition again (default off; env KDB_TAIL_FILES). A database with tail
  files needs a KDB from 2026-10-02 or later (kdb downgrade merges them back).
--snapshot-index-rows 1000000: a table with a full-text/JSON index, or an index not built yet, is
  scanned on a snapshot: only up to this many rows, a bigger one waits for its writer as before
  (default 1000000; 0 = indexed tables never get snapshots; env KDB_SNAPSHOT_INDEX_ROWS).
--txn-spill-mb 64: a transaction's log records past this many MB (or 1/16 of --memory-limit) go to a
  temporary file in the database's _tmp folder until COMMIT (default 64; 0 = never; env KDB_TXN_SPILL_MB).
--lanes on|off: priority lanes (default on; env KDB_LANES). A read whose shape used --heavy-ms of
  CPU or more the last three times it ran is heavy: only --heavy-slots of those run at once, at a
  lower CPU priority, and the rest wait in line. Charts and other short statements never wait. SET kdb.priority = high|normal|low, the X-KDB-Priority header
  and ALTER USER ... SET kdb.priority choose a lane by hand. Off: every statement runs at once.
--heavy-ms 100: the CPU time a read shape must use to count as heavy (env KDB_HEAVY_MS).
--heavy-slots N: heavy statements that run at once (default half the CPUs, at least 1; env
  KDB_HEAVY_SLOTS).
--heavy-queue N: heavy HTTP requests that may wait at once (default half the HTTP threads, at
  least 4; env KDB_HEAVY_QUEUE). Past it a request sent with priority low gets 503; a read heavy by
  its history runs at once. PostgreSQL statements are not counted: they wait up to their timeout.
--read-slots auto|off|N: reads (outside BEGIN) that run at once; the rest wait in line. A read holds
  its slot for at most 10 ms, so a long one never holds the line up (default auto = the CPUs the
  process may use, at least 2, following docker update --cpus; env KDB_READ_SLOTS). Off: every read
  starts at once, as before.
--timezone Asia/Kathmandu: the clock of CURRENT_DATE, ::date, date_trunc, EXTRACT and to_char, and of
  timer and backup schedules (default: the computer's own zone; env
  KDB_TIMEZONE; kdb.conf "timezone"). Times are still stored as UTC. A session: SET TIME ZONE.
  Default: the computer's own zone. Zones with summer time (America/New_York) need the system's
  zone files (tzdata); per user: ALTER USER name SET timezone = 'zone'.
--warm-indexes on|off: build every index right after a database opens, on a background thread,
  so the first query finds them ready (default on; env KDB_WARM_INDEXES). Off: on first use.
--sync-commits always: every write is on disk before it is confirmed (default).
--sync-commits batch: writes are confirmed at once and put on disk every 10 ms. Much faster for
  many small writes; a power cut can lose the last confirmed writes, but never damages the data.
Protection of kdb serve (docs/tutorial/21-protection.md):
  --statement-timeout 60s      stop statements that run longer (default 60s, 0 = no limit)
  --max-result-memory 1GB      rows one query may keep in memory (default 1GB, 0 = no limit)
  --max-groups N               GROUP BY groups of one query (default 5000000, 0 = no limit)
  --max-queries-per-user N     statements one user runs at the same time (default 32, 0 = no limit)
  --safe-updates               refuse UPDATE and DELETE without WHERE (SET safe_updates = off per session)
  --read-only                  the whole server only reads
  --standby-of URL             this server is a standby: it copies every database of the primary at URL
                               (http://host:9900) from its log, and only reads (env KDB_STANDBY_OF)
  --standby-token T            the token the standby sends to the primary: its --token or an admin's API
                               token (env KDB_STANDBY_TOKEN)
  --trash-days N               DROP TABLE keeps the table N days for UNDROP TABLE (default 7; also sql, shell)
  --audit on|off               audit log of logins, users, DDL, backups in _system (default on)
  --audit-keep-days N          remove audit rows older than N days (default 0 = keep forever)
  --activity-log-size N        recent events for the console's activity panel, in memory (default 5000, 0 = off)
  --slow-query-ms N            statements taking N ms or more go to <data>/_logs/slow_queries.jsonl
                               (GET /api/slow-queries; default 1000, 0 = off; env KDB_SLOW_QUERY_MS)
  --stat-statements on|off     pg_stat_statements: totals per statement shape, in memory
                               (default on; env KDB_STAT_STATEMENTS)
  --token-name NAME            what the audit log calls whoever uses the --token (default token; env KDB_TOKEN_NAME)
  --trash-max-mb N             a database's _trash may hold N MB; beyond it the oldest go at once
                               (default: a quarter of the data, at least 256 MB; 0 = no limit;
                               env KDB_TRASH_MAX_MB, kdb.conf trash_max_mb)
  --drain-timeout 30s          on stop, running requests may finish this long; new ones get 503 (default 30s)
  --tls-cert FILE --tls-key FILE  TLS on both ports: https:// and PostgreSQL sslmode=require (PEM files;
                               --tls-cert auto: a self-signed pair made in <data>/_tls; env KDB_TLS_CERT,
                               KDB_TLS_KEY; kdb.conf tls_cert, tls_key)
  --tls-required on|off        with TLS, refuse plain connections except from 127.x (default on;
                               env KDB_TLS_REQUIRED)
  --rate-limit-ip N            requests per second one client address may send, HTTP and PostgreSQL
                               (default 0 = no limit; 127.x is never limited; env KDB_RATE_LIMIT_IP)
  --rate-limit-user N          requests per second one user may send (default 0 = no limit;
                               env KDB_RATE_LIMIT_USER). Over a limit: HTTP 429 with Retry-After
  --http-threads N             HTTP requests handled at once (default: the CPU threads, at least 8; env KDB_HTTP_THREADS)
  --http-gzip remote|on|off    gzip answers of 2 KB or more for clients that send Accept-Encoding: gzip:
                               clients on other computers (remote, the default), all, or none (env KDB_HTTP_GZIP)
  --timer-workers N            timers that may run at the same time (1 to 64, default 3; env KDB_TIMER_WORKERS)
  --tcp-keepalives-idle S      TCP keepalive of every connection: seconds idle before the first probe,
  --tcp-keepalives-interval S  seconds between probes, and probes before a vanished client is dropped
  --tcp-keepalives-count N     (0 = the system's, the default; env KDB_TCP_KEEPALIVES_IDLE, ...)
  --ilp-auto-create on|off     POST /write creates a table it does not find, as QuestDB does (default on;
                               kdb.conf "ilp_auto_create"; env KDB_ILP_AUTO_CREATE)
  --max-connections N          PostgreSQL logins at once, all databases (default 100, 0 = no limit; env KDB_MAX_CONNECTIONS)
  --lock-timeout 5s            a writer waits this long for a busy table (default 5s, 0 = no limit; env KDB_LOCK_TIMEOUT;
                               a session: SET lock_timeout = '20s')
  --idle-in-transaction-timeout 30s  an open BEGIN ... COMMIT that sends nothing this long is rolled
                               back (default 30s, 0 = never; env KDB_IDLE_IN_TRANSACTION_TIMEOUT;
                               a session: SET idle_in_transaction_session_timeout = '5min')
  --trigger-retries N          a failed deferred trigger job runs again N times (1 s, 2 s, 4 s ... apart) before
                               its queue stops (default 3, 0 = never; env KDB_TRIGGER_RETRIES)
  --max-trigger-jobs N         deferred trigger jobs waiting per database; a write past it is refused with the
                               reason (default 10000, 0 = no limit; env KDB_MAX_TRIGGER_JOBS)
  --alert-webhook URL          POST failed trigger jobs as JSON to http://<IPv4>:<port>/<path>
                               (env KDB_ALERT_WEBHOOK, kdb.conf alert_webhook)
  --backups <dir>              backup folder (default <data folder>/_backups; "off" = none; or KDB_BACKUP_DIR)
  --backup-schedule "daily 16:00"  verified backups of every database (local time; or KDB_BACKUP_SCHEDULE)
  --backup-command "<cmd>"     run after each backup to copy it off the server: {dir} {database} {name}
                               (or KDB_BACKUP_COMMAND), e.g. "rclone copy {dir} remote:kdb/{database}/{name}"
  --backup-copy <dir>          a second, checked copy of each scheduled backup (another disk)
  --backup-warn-hours N        warn in the log when the last good backup is older (default 26)
  --keep-daily N --keep-weekly N   backups kept (default 7 and 4; or KDB_BACKUP_KEEP_DAILY / _WEEKLY)
Exit codes: 0 OK, 1 error, 2 damage found, 3 the database needs a newer KDB (nothing was changed).

Help topics (0), from ./help-placeholder. "kdb help <topic>" prints one; a running server has them at /help:

kdb version

Syntax:

kdb version
kdb --version
kdb version <db>

Prints the version of KDB and the stored-format features it can read. crc32c hardware: yes means the CPU computes checksums (CRC32C) with special fast instructions. no also works, only slower. With <db> it shows the features that database uses (versions and upgrades).

Command line

kdb version

Output:

kdb 0.2.0-dev (crc32c hardware: yes)
tls: OpenSSL 3.6.3 9 Jun 2026

Stored-format features this build reads (51):
  views                0.2.0-dev (2026-09-10)   CREATE VIEW: the "views" file
  partition_format_2   0.2.0-dev (2026-09-11)   table partition files in format 2 (one raw block per column)
  block_stats          0.2.0-dev (2026-09-11)   column stats section in partition files (count, min, max, sum per column)
  runs                 0.2.0-dev (2026-09-11)   log record 'C': rows of a bulk upsert kept as columns
  auto_meta            0.2.0-dev (2026-09-11)   AUTO id counters in the manifest (auto.<table id>)
  json                 0.2.0-dev (2026-09-11)   JSON columns (type code 12)
  bytes                0.2.0-dev (2026-09-11)   BYTES columns (type code 13)
  check                0.2.0-dev (2026-09-11)   CHECK constraints (catalog format 2, part 'C')
  indexes              0.2.0-dev (2026-09-11)   CREATE INDEX, UNIQUE and FULLTEXT indexes (catalog part 'I')
  routines             0.2.0-dev (2026-09-11)   functions, procedures and triggers: the "routines" file
  users                0.2.0-dev (2026-09-11)   server users and roles (_system.users)
  audit                0.2.0-dev (2026-09-12)   SET AUDIT ON: catalog part 'A', log record 'P', the _audit_log table
  table_grants         0.2.0-dev (2026-09-12)   GRANT ... ON TABLE (_system.users.table_grants)
  totp                 0.2.0-dev (2026-09-12)   two-factor logins (_system.users.totp)
  trash                0.2.0-dev (2026-09-12)   dropped tables kept as _dropped_<ms>_<name> for UNDROP TABLE
  database_settings    0.2.0-dev (2026-09-12)   ALTER DATABASE ... SET READ ONLY (_system.database_settings)
  dialects             0.2.0-dev (2026-09-12)   ALTER DATABASE ... SET dialect (_system.database_dialects)
  pg_catalog_parts     0.2.0-dev (2026-09-12)   PostgreSQL column types, multi-column keys, foreign keys, expression and partial indexes (catalog parts 'T', 'K', 'F', 'X')
  sequences            0.2.0-dev (2026-09-12)   CREATE SEQUENCE: the "sequences" file and its log records
  batched_log_sync     0.2.0-dev (2026-09-11)   log segments in the batched format (header flag 1, 16-byte commit records): group commit and --sync-commits batch
  dedup_change         0.2.0-dev (2026-09-12)   ALTER TABLE ... DEDUP ENABLE / DISABLE: the DEDUP KEY of a table that already holds rows
  plpgsql_triggers     0.2.0-dev (2026-09-12)   PostgreSQL plpgsql trigger functions and FOR EACH ROW triggers in the "routines" file
  cached_views         0.2.0-dev (2026-09-13)   CREATE CACHED VIEW: which views are cached, and REFRESH ON COMMIT, in the "views" file
  deferred_triggers    0.2.0-dev (2026-09-13)   CREATE TRIGGER ... DEFERRED: job records in the log, and the "trigger_queue" and "q/<job>" files of queued jobs
  materialized_views   0.2.0-dev (2026-09-13)   CREATE MATERIALIZED VIEW: catalog part 'M', REFRESH MATERIALIZED VIEW log records, mv_dirty.<id> in the manifest
  table_ttl            0.2.0-dev (2026-09-13)   TTL n DAYS on a table: catalog part 'L'
  read_only_procedures 0.2.0-dev (2026-09-24)   CREATE PROCEDURE ... READ ONLY in the "routines" file
  dedup_insert         0.2.0-dev (2026-09-24)   WITH (dedup_insert = 'upsert') on a table: catalog part 'U'
  decimal128           0.2.0-dev (2026-09-25)   NUMERIC(p, s) columns with more than 18 digits: 128-bit DECIMAL (type code 14) in the catalog, partition files and log
  dedup_generated_key  0.2.0-dev (2026-09-25)   a stored generated column in a DEDUP KEY, like the minute of the time (an older build refuses such a catalog)
  symbol_partitions    0.2.0-dev (2026-09-25)   PARTITION BY <time>, SYMBOL(col): catalog part 'P' (one partition file per time bucket and symbol, t/<id>/<time>/s-...)
  round_time_zone      0.2.0-dev (2026-09-25)   TIMESTAMP ROUND ... TIME ZONE 'zone' columns: catalog part 'R'
  enum_types           0.2.0-dev (2026-09-25)   CREATE TYPE ... AS ENUM: the ENUM types after the tables of the catalog (catalog format 3)
  row_ttl              0.2.0-dev (2026-09-25)   CREATE TABLE ... WITH (ttl_column = c): rows expire one by one, catalog part 'E'
  column_compression   0.2.0-dev (2026-09-25)   ALTER TABLE ... SET (compression = on): catalog part 'Z' and packed partition files (header flag 2)
  calendars            0.2.0-dev (2026-09-25)   CREATE CALENDAR: trading calendars (eras, holidays, extra trading days) after the ENUM types of the catalog (catalog format 4)
  materialized_view_sample_by 0.2.0-dev (2026-09-26)   CREATE MATERIALIZED VIEW ... AS (SELECT ... SAMPLE BY ...): a SAMPLE BY query in catalog part 'M', refreshed bucket by bucket
  comments             0.2.0-dev (2026-09-26)   COMMENT ON TABLE / COLUMN: the comments of a table and its columns, catalog part 'N'
  cache_tables         0.2.0-dev (2026-10-02)   WITH (max_bytes, evict, durability): cache tables with a memory cap and eviction, catalog part 'Y'
  resident_tables      0.2.0-dev (2026-10-02)   WITH / SET (resident = on): a table never unloaded to make room, catalog part 'H'
  partition_tails      0.2.0-dev (2026-10-02)   tail files: rows appended to a partition saved in extra files t/<id>/<partition>.tail<k>.b<version> (tail_files)
  uuid                 0.2.0-dev (2026-10-02)   UUID columns: 16 bytes per value (type code 15) in the catalog, partition files and log
  search_analyzer      0.2.0-dev (2026-10-02)   CREATE FULLTEXT INDEX ... WITH (language = 'english'): the word analyzer of a full-text index, catalog part 'S'
  column_grants        0.2.0-dev (2026-10-02)   GRANT / REVOKE on single columns (_system.users.column_grants)
  row_policies         0.2.0-dev (2026-10-02)   row-level security: CREATE POLICY and ALTER TABLE ... ENABLE ROW LEVEL SECURITY (_system.row_policies)
  partition_deltas     0.2.0-dev (2026-10-02)   delta files: a few rows of a partition replaced or deleted, saved in t/<id>/<partition>.delta.b<version>
  fulltext_segments    0.2.0-dev (2026-10-02)   full-text and JSON index segments in fts/*.kfs (the words of each partition's rows; never recorded: an older build ignores them)
  compressed_log       0.2.0-dev (2026-10-02)   packed log records (type 3): the data records of a big commit written compressed (KDB_WAL_COMPRESS)
  endpoints            0.2.0-dev (2026-10-03)   CREATE ENDPOINT: saved queries served at GET /v/<name>, kind 'E' in the routines file
  rules                0.2.0-dev (2026-10-03)   CREATE RULE: named checks, session hours and settings of a table (catalog part 'Q')
  sync_commit_tables   0.2.0-dev (2026-10-03)   WITH (synchronous_commit = on | off) of a table: catalog part 'G'

A database that uses a feature not in this list needs a newer KDB (kdb check exits with 3).

<db>: a name or a folder path

Syntax:

kdb sql market "..."          -- a name: the database 'market' in the data folder
kdb sql ./market "..."        -- a path: the folder 'market' in the current folder
kdb sql D:\kdb\market "..."   -- a path: always that folder

The commands sql, shell, serve, info, check, repair and backup take <db>. KDB decides like this:

You writeKDB uses
A name (only letters, digits, _ and -), and the data folder has a database with that nameThat database in the data folder. Upper or lower case does not matter.
A name, and the data folder has no database with that nameThe folder with that name in the current folder. It is created when it does not exist.
A path (it has \, /, : or .)Always that folder. It is created when it does not exist (not by kdb info and kdb check).
Nothing (only kdb shell and kdb serve)The data folder: kdb shell opens the database default; kdb serve serves all databases.

Command line: A name in the data folder, and a path in the current folder

kdb shell
> CREATE DATABASE reports;
kdb sql reports "CREATE TABLE daily (day DATE PRIMARY KEY, trades LONG); INSERT INTO daily VALUES ('2026-09-10', 32)"
kdb info reports
kdb sql ./reports "SHOW TABLES"

Output:

> kdb shell

KDB 0.2.0-dev shell on default (data folder ./kdb-data)
End statements with ';'. Type .help for commands, .exit to quit.
kdb> database 'reports' created
kdb>

> kdb sql reports "CREATE TABLE daily (day DATE PRIMARY KEY, trades LONG); INSERT INTO daily VALUES ('2026-09-10', 32)"

table 'daily' created

1 row(s) inserted

> kdb info reports

database        ./kdb-data/reports
manifest        copy a, sequence 7
last shutdown   clean
log position    4 (saved in files up to 4)
newest change   2026-10-22 11:28:31
features        batched_log_sync, block_stats, compressed_log, partition_format_2
manifest keys   7

FILE                                      VERSION       SIZE          LSN  STATUS
catalog                                         1      232 B            4  ok
t/1/all                                         2      388 B            4  ok

2 files, 620 B, 0 marked damaged
(STATUS is what the manifest records. To read every block and find new damage, run: kdb check)

> kdb sql ./reports "SHOW TABLES"

name | kind | partition | columns | rows
-----+------+-----------+---------+-----
(0 rows, 0.0 ms)

In this example the data folder is ./kdb-data. kdb info reports opens ./kdb-data/reports. ./reports is a new, empty database in the current folder, so it has no tables.

The data folder

Syntax:

--data-dir <folder>
KDB_DATA_DIR=<folder>

The data folder holds many databases, one sub-folder per database. KDB looks for it in this order; the first one that is set wins:

#WhereExample
1The option --data-dir <folder>kdb serve --data-dir E:\kdb-data
2Only kdb serve: data_dir in kdb.conf, what the console's Settings page saved (kdb.conf)"data_dir": "E:/kdb-data"
3The environment variable KDB_DATA_DIR$env:KDB_DATA_DIR = "E:\kdb-data"
4The folder data next to the kdb programD:\KDB\build\bin\data

The log on another disk

Syntax:

KDB_WAL_DIR=<folder>
"wal_dir": "<folder>"        in kdb.conf (kdb serve only)

Every commit writes the database's log and waits for the disk to confirm it. When the data folder is on a slow-to-sync disk (a Windows folder bind-mounted into Docker Desktop confirms a small write in about 1 ms; a Docker volume inside the VM in 0.4 ms), wal_dir puts the log on the faster disk while the data files stay where they are. Each database gets a sub-folder of it, named like the database. KDB leaves a note in the database's own wal folder saying where its log went, so kdb backup, a repair and a server started without the setting still find it. A database that already has a log keeps writing its current segment where it is; the next segment goes to the new folder. The old log kept for repairs (wal_archive) stays in the data folder.

Command line: --data-dir wins over KDB_DATA_DIR

kdb shell --data-dir my-data
> CREATE DATABASE shop;
> SHOW DATABASES;
kdb serve --data-dir my-data

Output:

> kdb shell --data-dir my-data

KDB 0.2.0-dev shell on default (data folder my-data)
End statements with ';'. Type .help for commands, .exit to quit.
kdb> database 'shop' created
kdb> name    | tables | bytes | current
--------+--------+-------+--------
default |      0 |     0 | true   
shop    |      0 |     0 | false  
(2 rows, 0.5 ms)
kdb>

> kdb serve --data-dir my-data

KDB 0.2.0-dev
  console   http://127.0.0.1:9900
  data      my-data (from --data-dir; one folder per database)
  default   default (used when a request names no database)
  backups   my-data/_backups/<database name> (from default)
  access    no token
  capacity  32 HTTP threads (default), 500 PostgreSQL connections (default), lock wait 5 s (default)
  lanes     reads using 100 ms of CPU (three times in a row) wait for one of 16 heavy slots, at most 16 HTTP in line (--lanes on)
  reads     32 reads run at once (the CPUs, at least 2; follows the CPU limit), the rest wait in line (--read-slots)
  commits   synced to disk before they are confirmed (--sync-commits always)
  time zone Asia/Katmandu (the computer's); also timers and backup schedules
  readers   read while a long trigger or CALL writes (--reader-yield on); idle open transaction rolled back after 30 s (default)
  memory    26.95 GB for the whole process (85% of the machine's memory); table data 512.0 MB of it (--memory)
  cpu       32 CPUs (the machine's); 16 query threads (half of the CPUs, follows the limit), limits checked every 10 s
  limits    statement timeout 1 min, result memory 1.00 GB, 5000000 groups, 32 statements per user
  safety    writes allowed; DROP TABLE kept 7 days (UNDROP TABLE); audit log on (SHOW AUDIT)
  logs      slow queries >= 1000 ms to my-data/_logs/slow_queries.jsonl; pg_stat_statements on; metrics at /metrics
  recovery  manifest copy: a, last shutdown: clean
  open      opened in 1 ms: manifest 0 ms, 0 files read 0 ms, log check 0 ms, replay 1 ms (0 transactions), clean-up 0 ms

Press Ctrl+C to stop.

The banner of kdb serve says where the data folder came from: from --data-dir, from kdb.conf, from KDB_DATA_DIR or from program folder.

To set KDB_DATA_DIR for good (these lines are not run here):

# Windows, for your user. Open a new PowerShell window afterwards.
[Environment]::SetEnvironmentVariable("KDB_DATA_DIR", "E:\kdb-data", "User")
# Linux and macOS: add this line to ~/.bashrc or ~/.zshrc
export KDB_DATA_DIR=/srv/kdb/data

Exit codes

Syntax:

0   OK
1   error
2   damage found, or damage that could not be repaired
3   the database needs a newer KDB (nothing was changed)

Every kdb command ends with an exit code. Scripts use it to know if the command worked.

CodeMeaningExamples
0OKThe query ran. The backup was made. No damage found.
1ErrorWrong SQL. A duplicate key. The database is open in another program. Wrong arguments.
2Damagekdb check found damage. A data file failed its checksum. A repair could not fix everything.
3Needs a newer KDBA newer KDB wrote the database. It is not damaged: open it with that KDB (versions).

Command line: 0 for a good query, 1 for a wrong table name (sample data: sample)

kdb sql market "SELECT count(*) AS n FROM brokers"
kdb sql market "SELECT count(*) FROM brokerz"

Output:

> kdb sql market "SELECT count(*) AS n FROM brokers"

n
-
5
(1 row, 0.3 ms)

> kdb sql market "SELECT count(*) FROM brokerz"

error: line 1, column 22: table or view 'brokerz' does not exist

exit code 1

# In a script (not run here)
kdb check D:\kdb\market | Out-Null
switch ($LASTEXITCODE) {
  0 { "check: OK" }
  2 { "check: DAMAGE FOUND - run kdb repair" }
  3 { "check: this database needs a newer KDB - do not repair it" }
  default { "check: could not run (exit code $LASTEXITCODE)" }
}

How options are read

Syntax:

kdb <command> <arguments> [--option value] [--flag]

Options can come before or after the other arguments. Some options take a value (--format csv), some do not (--hard-links).

OptionValueUsed by
--data-dirfoldersql, shell, serve, info, check, repair, backup, user
--backupsfoldersql, shell, serve, repair
--memorymegabytessql, shell, serve
--format, --max-rowstable/csv/json, a numberkdb sql
--port, --bind, --token, --db, --threads, --cache-mb, --sync-commits, --pg-port, --pg-timestamptz, --config, --warm-indexes, --reader-yield, --drain-timeoutsee kdb serveserve
--statement-timeout, --max-result-memory, --max-groups, --max-queries-per-user, --safe-updates, --read-only, --trash-days, --audit, --audit-keep-days, --activity-log-sizesee protectionserve (--trash-days also sql, shell)
--backup-schedule, --backup-copy, --backup-warn-hourssee backup scheduleserve
--dir, --url, --token, --homefolder, address, secret, folderkdb skill install
--hard-links, --keep-daily, --keep-weekly-, a number, a numberkdb backup
--logs-from, --untilfolder, timekdb restore
--accept-log-loss-kdb repair
--roleadmin/write/read/nonekdb user add

Command line: A misspelled option with a value (sample data: sample)

kdb sql market "SELECT 1" --formt csv

Output:

usage: kdb sql <db> "SQL" [--format table|csv|json] [--max-rows N] [--backups <dir>]

exit code 1