# kdb serve > Start the KDB server - web console, HTTP API, line protocol and PostgreSQL port - on one database or on the whole data folder. - Category: Command line - Keywords: server, start server, port 9900, web console, HTTP API, banner, Ctrl+C, stop server, SIGTERM, systemd, service, many databases, default database, --port, --bind, --token, --db, --threads, --cache-mb, --warm-indexes, KDB\_WARM\_INDEXES, --reader-yield, KDB\_READER\_YIELD, reader yield, --per-table-reads, KDB\_PER\_TABLE\_READS, --read-only-calls, KDB\_READ\_ONLY\_CALLS, --scoped-ddl, KDB\_SCOPED\_DDL, --snapshot-reads, KDB\_SNAPSHOT\_READS, snapshot reads, --tail-files, KDB\_TAIL\_FILES, tail files, --sync-commits, --pg-port, --pg-timestamptz, --pg-int-division, KDB\_PG\_INT\_DIVISION, 0.0.0.0, result cache, --drain-timeout, KDB\_DRAIN\_TIMEOUT, drain, graceful shutdown, 503, Retry-After, kdb.conf, KDB\_WAL\_ARCHIVE\_MB, KDB\_KEEP\_VERSIONS, KDB\_TRASH\_KEEP\_HOURS, worker threads, --http-threads, KDB\_HTTP\_THREADS, http\_threads, --http-gzip, KDB\_HTTP\_GZIP, gzip answers, --max-connections, KDB\_MAX\_CONNECTIONS, max\_connections, --lock-timeout, KDB\_LOCK\_TIMEOUT, lock\_timeout\_ms, --idle-in-transaction-timeout, KDB\_IDLE\_IN\_TRANSACTION\_TIMEOUT, idle\_in\_transaction\_timeout\_ms, --memory-limit, KDB\_MEMORY\_LIMIT, KDB\_LEVEL\_FROM\_HEAP, memory limit, memory full, out of memory, 53200, OOM, container memory, --slow-query-ms, KDB\_SLOW\_QUERY\_MS, slow\_query\_ms, --token-name, KDB\_TOKEN\_NAME, --trash-max-mb, KDB\_TRASH\_MAX\_MB, trash\_max\_mb, metrics, self-tuning, cgroup, cpu.max, --cpus, docker update, CPU limit, KDB\_SELF\_TUNING\_SECONDS, --pin-threads, KDB\_PIN\_THREADS, pin threads, CPU pinning, sched\_setaffinity, cpuset, pinned\_cpus, --snapshot-index-rows, KDB\_SNAPSHOT\_INDEX\_ROWS, --txn-spill-mb, KDB\_TXN\_SPILL\_MB, txn\_spill\_mb, --ddl-beside-writers, KDB\_DDL\_BESIDE\_WRITERS, --skip-locked-beside, KDB\_SKIP\_LOCKED\_BESIDE, SKIP LOCKED, options, all options, every option, settings, environment variables, --timer-workers, KDB\_TIMER\_WORKERS, timer\_workers, KDB\_TTL\_CHECK\_MS, KDB\_LAZY\_OPEN, KDB\_CACHE\_MB, KDB\_TOKEN, KDB\_MEMORY, --timezone, KDB\_TIMEZONE, --trigger-retries, KDB\_TRIGGER\_RETRIES, --max-trigger-jobs, KDB\_MAX\_TRIGGER\_JOBS, --alert-webhook, KDB\_ALERT\_WEBHOOK, alert\_webhook, rate\_limit\_ip, rate\_limit\_user, KDB\_RATE\_LIMIT\_IP, KDB\_RATE\_LIMIT\_USER, tls\_cert, tls\_key, KDB\_TLS\_CERT, KDB\_TLS\_KEY, KDB\_TLS\_REQUIRED, http\_functions, http\_timeout\_ms, KDB\_HTTP\_FUNCTIONS, KDB\_HTTP\_TIMEOUT\_MS, KDB\_BACKUP\_DIR, KDB\_BACKUP\_SCHEDULE, KDB\_BACKUP\_KEEP\_DAILY, KDB\_BACKUP\_KEEP\_WEEKLY, KDB\_BACKUP\_COMMAND, test switches, KDB\_CHECKPOINT\_BESIDE\_WRITERS, checkpoint beside writers, KDB\_NO\_ZONE\_MAPS, KDB\_NO\_COLUMN\_MASK, KDB\_NO\_SYMBOL\_PRUNING, KDB\_NO\_TIME\_SPANS, KDB\_NO\_PARTITION\_ANSWERS, KDB\_NO\_LATEST\_PARTITIONS, KDB\_NO\_NEWEST\_FIRST, KDB\_NO\_TEXT\_KEYS, KDB\_NO\_VECTOR\_EXPR, KDB\_NO\_VECTOR\_CASE, KDB\_NO\_VECTOR\_DISTINCT, KDB\_NO\_UNION\_PUSHDOWN, KDB\_NO\_VIEW\_PUSHDOWN, KDB\_NO\_SUBQUERY\_STREAM, KDB\_NO\_JOIN\_LOOKUP, KDB\_NO\_JOIN\_PLACES, KDB\_NO\_KEY\_STRETCH, KDB\_NO\_POST\_WINDOW\_FILTER, KDB\_NO\_WINDOW\_TOP\_PER\_KEY, KDB\_WINDOW\_NO\_FAST, KDB\_WINDOW\_NO\_VECTOR\_SCAN, KDB\_NO\_INSERT\_STREAM, KDB\_NO\_INSERT\_RUNS, KDB\_NO\_INSERT\_APPLY, KDB\_NO\_BATCH\_DEDUP, KDB\_NO\_FOREIGN\_PUSH, KDB\_NO\_VECTOR\_PRESELECT, KDB\_NO\_PREFETCH, KDB\_MV\_NO\_DEFER, KDB\_WRITE\_TRACE, KDB\_CACHE\_VERIFY, KDB\_INDEX\_MAX\_VISITS, --stat-statements, KDB\_STAT\_STATEMENTS, lanes, priority lanes, admission control, --lanes, KDB\_LANES, --heavy-ms, KDB\_HEAVY\_MS, --heavy-slots, KDB\_HEAVY\_SLOTS, --heavy-queue, KDB\_HEAVY\_QUEUE, --read-slots, KDB\_READ\_SLOTS, read slots, docker --cpus throughput, kdb.priority, X-KDB-Priority, heavy query, report, chart latency, 53000, server is busy, --log-lock-waits, KDB\_LOG\_LOCK\_WAITS, --deadlock-timeout, KDB\_DEADLOCK\_TIMEOUT, log\_lock\_waits, deadlock\_timeout, --tcp-keepalives-idle, --tcp-keepalives-interval, --tcp-keepalives-count, KDB\_TCP\_KEEPALIVES\_IDLE, tcp\_keepalives\_idle, TCP keepalive, --standby-of, --standby-token, KDB\_STANDBY\_OF, standby, kdb promote - See also: [The kdb program](cli-overview.md), [Web console](web-console.md), [Run SQL over HTTP (/exec, /exp)](http-exec.md), [Databases over HTTP (/api/databases)](http-databases.md), [Keeping kdb serve safe](server-security.md), [Users and roles](users-and-roles.md), [PostgreSQL protocol (port 8832)](postgres-protocol.md), [Line protocol (POST /write)](line-protocol.md), [Performance tips](performance-tips.md) `kdb serve` starts the server. It keeps running until you press Ctrl+C. While it runs, programs use the databases through it: the [web console](web-console.md) in a browser, the [HTTP API](http-exec.md), the [line protocol](line-protocol.md) (`POST /write`) and PostgreSQL tools on the [PostgreSQL port](postgres-protocol.md). The server has the database files open. So `kdb sql`, `kdb shell`, `kdb backup` and `kdb check` on the same database fail while it runs. Use the server instead: the console, `POST /exec`, `BACKUP DATABASE`, `POST /api/check`. ## kdb serve Syntax: ```sql kdb serve [] [--data-dir ] [--config ] [--db ] [--port 9900] [--bind 127.0.0.1] [--backups ] [--token ] [--memory ] [--memory-limit ] [--threads N] [--cache-mb ] [--sync-commits always|batch] [--pg-port 8832] [--pg-timestamptz] [--pg-int-division on|off] [--warm-indexes on|off] [--reader-yield on|off] [--per-table-reads on|off] [--read-only-calls on|off] [--scoped-ddl on|off] [--ddl-beside-writers on|off] [--skip-locked-beside on|off] [--log-lock-waits on|off] [--deadlock-timeout 1s] [--snapshot-reads on|off] [--snapshot-index-rows N] [--txn-spill-mb 64] [--tail-files on|off] [--table-writers one|many] [--timezone ] [--drain-timeout 30s] [--pin-threads auto|on|off] [--http-threads N] [--max-connections N] [--timer-workers N] [--lock-timeout 5s] [--idle-in-transaction-timeout 30s] [--trigger-retries N] [--max-trigger-jobs N] [--alert-webhook ] [--slow-query-ms N] [--token-name ] [--trash-max-mb N] [--tls-cert --tls-key ] [--tls-required on|off] [--lanes on|off] [--heavy-ms 100] [--heavy-slots N] [--heavy-queue N] [--read-slots auto|off|N] [protection and backup options: see protection and backup-schedule] ``` | Option | Meaning | Default | |---|---|---| | `` | Without it: **every** database in the data folder. A name from the data folder: every database, and this one is the default. A folder path: **only** that folder. | all databases in the data folder | | `--data-dir ` | The data folder (see [the data folder](cli-overview.md#data-folder)). Always wins over `kdb.conf`. | `kdb.conf`, else `KDB_DATA_DIR`, else `data` next to kdb | | `--config ` | Settings the console's [Settings page](console-settings.md) may change: the data folder, and `wal_archive_mb` (old log kept per database; it wins over the `KDB_WAL_ARCHIVE_MB` environment variable, default 1024). | `kdb.conf` next to kdb | | `--db ` | The **default** database: the one a request uses when it names no database. With a folder path: the name the folder is served under. | `default` (with a path: the folder's name) | | `--port N` | The HTTP port for the console and the API (1 to 65535) | `9900` | | `--bind
` | The network address to listen on. `127.0.0.1` = only this computer. `0.0.0.0` = every network. | `127.0.0.1` | | `--backups ` | Backup folder for `BACKUP DATABASE`, the console's **Backup now**, `POST /api/backup` and the [schedule](backup-schedule.md). A database of the data folder backs up into `/`. Damaged files are also repaired from here when a database opens. `off` = no backups. Also `KDB_BACKUP_DIR` or `backup_dir` in `kdb.conf`. | `_backups` in the data folder; for one folder served by its path, `_backups/` next to that folder | | `--token ` | A secret every request must send (`Authorization: Bearer `). It works like an admin user. `kdb serve` reads no environment variable for it; the Docker image passes its `KDB_TOKEN` as `--token`. | no token | | `--memory ` | Memory for table data kept loaded, for all open databases together. Data over it is read from disk again when needed. No environment variable in the program; the Docker image passes its `KDB_MEMORY` as `--memory`. | `512`, but without the flag at most 40% of the [memory limit](#memory-limit) | | `--memory-limit ` | One limit for **everything** the server holds: table data, unsaved changes, indexes, results and caches (see [memory limit](#memory-limit)). `3GB`, `512MB`, or a plain number of megabytes. `0` or `off` = no limit. Also `KDB_MEMORY_LIMIT`. | 85% of a container's memory limit, else of the machine's memory | | `--threads N` | Threads for big queries and writes, shared by all databases. From 1 to half of the machine's CPU threads. A number set here stays; without it the count follows the CPU limit ([self-tuning](#self-tuning)). | half of the CPUs the process may use | | `--cache-mb ` | Memory for results of repeated queries, for all open databases together. `0` = no result cache. Else `cache_mb` in kdb.conf, else `KDB_CACHE_MB`. One result may use a quarter of it, a [pinned](pinned-queries.md) one three quarters. | `256`, but at most a tenth of the [memory limit](#memory-limit) | | `--warm-indexes on\|off` | Build every index of a database on a background thread right after it opens, so the first query finds it ready (see [indexes](indexes.md#warm)). `off` = build on first use. Also `KDB_WARM_INDEXES`. | `on` | | `--read-only-calls on\|off` | `on`: a `CALL` of a procedure that changes nothing runs like a `SELECT`: no table locks, no transaction ([a CALL that only reads](procedures.md#read-only-call)). `off`: every `CALL` is a transaction. Also `KDB_READ_ONLY_CALLS`. | `on` | | `--scoped-ddl on\|off` | `on`: `CREATE`, `ALTER` and `DROP TABLE`, `CREATE` and `DROP INDEX` and `CREATE`, `ALTER` and `DROP TRIGGER`, `TRUNCATE`, `CREATE TABLE ... AS` and materialized views lock only their tables, and views, procedures and functions lock none, so they do not wait for a long writer of other tables ([schema changes](transactions.md#one-writer)). `off`: every schema change locks every table. Also `KDB_SCOPED_DDL`. | `on` | | `--ddl-beside-writers on\|off` | With `--scoped-ddl on`: `CREATE TABLE`, `ALTER TABLE`, `RENAME TABLE` and `CREATE` / `DROP INDEX` run beside writers of other tables, also beside one long statement that never pauses (a big `INSERT ... SELECT` or `COPY`), instead of failing with `55P03` after the lock timeout ([schema changes](transactions.md#one-writer)). `off`: they wait until such a writer pauses between two statements, as before 2026-09-25. Also `KDB_DDL_BESIDE_WRITERS`. | `on` | | `--skip-locked-beside on\|off` | `on`: `SELECT ... FOR UPDATE SKIP LOCKED` inside `BEGIN` returns the rows another open transaction did not lock or change, although that transaction holds the table. Your `UPDATE` of such a row then waits for its `COMMIT` and fails with `40001` after the lock timeout ([FOR UPDATE](transactions.md#for-update)). `off`: no rows there, as before; without `BEGIN` the free rows are returned either way. Also `KDB_SKIP_LOCKED_BESIDE`. | `off` | | `--reader-yield on\|off` | `on`: a reader of a table a long trigger, `CALL` or transaction is writing does not wait for it. The writer steps aside between its statements and the reader sees the last committed rows. `off`: such readers wait for the writer's whole request. Also `KDB_READER_YIELD`. The start-up line `readers` says which is in use. | `on` | | `--pin-threads auto\|on\|off` | Linux. `auto`: 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 ([pinning threads](#pin-threads)). `on`: each HTTP worker, PostgreSQL connection and query thread runs on one CPU of its own, so its CPU cache stays warm. `off`: nothing is pinned. Windows: ignored (`on` with a warning). Also `KDB_PIN_THREADS`. | `auto` | | `--per-table-reads on\|off` | `on`: a reader waits only for writers of the tables it reads, and not while a checkpoint writes its files (see [transactions](transactions.md#per-table-reads)). `off`: every reader waits for every writer. Also `KDB_PER_TABLE_READS`. | `on` | | `--snapshot-reads on\|off` | `on`: a reader of a table a long `CALL`, trigger job or big statement is writing does not wait at all: it reads a snapshot of the committed rows (see [transactions](transactions.md#snapshot-reads)). Tables with an index too, while a writer is busy on them: such a read still uses a plain or expression index, on the snapshot. Also `KDB_SNAPSHOT_READS`. The start-up line `readers` says so. | `off` | | `--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 - a one-row `INSERT`, `UPDATE ... WHERE id = x`, `DELETE ... WHERE id = x`, a `CALL` or trigger made of such statements. The same row is waited for, as in PostgreSQL; two writers that wait for each other's rows get `deadlock` (`40P01`). Anything else on the table (a range `UPDATE`, `INSERT ... SELECT`, a `SELECT` statement, `BEGIN ... COMMIT` across requests, a table with foreign keys or other `UNIQUE` columns) takes the whole table, as before ([several writers of one table](transactions.md#shared-writers)). `one`: one writer per table at a time. Also `KDB_TABLE_WRITERS`. | `one` | | `--snapshot-index-rows N` | With snapshot reads on: a plain or expression index is used on a snapshot at any table size. A table with a full-text or JSON index, or an index not built in memory yet, has its snapshot scanned instead, and only while it has at most this many rows; a bigger one is waited for, as before, so a lookup does not turn into a scan of millions of rows. `0`: indexed tables are always waited for. Also `KDB_SNAPSHOT_INDEX_ROWS`. | `1000000` | | `--tail-files on\|off` | `on`: a save (checkpoint) puts rows appended after the end of a partition in a small [tail file](partitions-and-dedup.md#tail-files) next to it, instead of writing the whole partition again. A database with tail files needs KDB 2026-10-02 or newer. Also `KDB_TAIL_FILES`. | `off` | | `--txn-spill-mb N` | A transaction's log records past this many MB (or 1/16 of `--memory-limit`, if less) go to a temporary file in the database's `_tmp` folder until `COMMIT`, so a long `CALL` or trigger job does not hold them all in memory (see [transactions](transactions.md#txn-spill)). `0`: they all stay in memory. Also `KDB_TXN_SPILL_MB`. | `64` | | `--sync-commits always` | Every write is on disk before KDB confirms it. | `always` | | `--sync-commits batch` | Writes are confirmed at once and put on disk every 10 ms. | | | `--pg-port N` | Port for the PostgreSQL protocol. `0` = off. | `8832` | | `--pg-timestamptz` | Send `TIMESTAMP` columns to PostgreSQL clients as `timestamptz` (with time zone). | off | | `--timezone ` | The server's time zone, like `Asia/Kathmandu`: the clock of `CURRENT_DATE`, `::date`, `date_trunc` and `EXTRACT` for sessions that set none, and of timer and backup schedules. Times are still stored in UTC. Env `KDB_TIMEZONE`, kdb.conf `"timezone"`. See [time zones](time-and-time-zones.md#session-time-zone). | UTC (schedules: the machine's clock) | | `--pg-int-division on\|off` | `on`: on the Postgres port, whole number / whole number gives a whole number (`7 / 2` is `3`), as in PostgreSQL. A connection can still choose with `SET int_division = integer \| float`. Also `KDB_PG_INT_DIVISION`. See [integer division](postgres-protocol.md#int-division). | `off` | | `--pg-compat on\|off` | `on`: PostgreSQL 14's answers (exact decimal literals, `RANGE` window frames, locale text order, ...) for every database without its own `ALTER DATABASE ... SET pg_compat`. A session can still choose with `SET kdb.pg_compat`. Also `KDB_PG_COMPAT`. Waiting for the owner's decision: keep it off. See [PostgreSQL-compatible results](postgres-protocol.md#pg-compat). | `off` | | `--drain-timeout