Skip to main content

Configuration

ScramDB reads its settings from a single TOML file. Pass the path with -c (or --config):

scramdb --config /etc/scramdb/config.toml

If you do not pass --config, ScramDB uses scramdb-config.toml in the working directory. The path is exact: ScramDB does not search parent directories or other locations for it.

The official Docker image ships a ready-to-use file at /etc/scramdb/config.toml. Mount your own over that path to override it:

docker run -v ./my-config.toml:/etc/scramdb/config.toml scramdb:latest

An unrecognized top-level key in the file is a hard parse error, not a silently ignored typo. ScramDB refuses to start rather than run with a config it can't fully account for; check the startup log for the exact key name if a config change stops the server from starting. See Troubleshooting.

A minimal config​

[storage] is the only section with no default of its own: prod_name, shard_id, and basedir are always required, plus one of buffer_pool_size_bytes or buffer_pool_percent to size the buffer pool. Every other section is optional and falls back to sensible defaults, so you set only what you want to change.

[storage]
prod_name = "tundra"
shard_id = 0
basedir = "/var/lib/scramdb" # where data and the write-ahead log live
buffer_pool_percent = 60 # cache for hot data pages, as a share of detected RAM
execution_memory_percent = 15 # shared pool for query execution

Sizing works either way: pin an exact size with buffer_pool_size_bytes = "2GB", or let it auto-size from RAM with buffer_pool_percent. Byte sizes accept human-readable strings like "64MB", "2GB", or "64GB", or a plain number of bytes. Percent-based sizing reads the container's cgroup memory limit first, falling back to host memory, so it stays correct under docker run --memory or a Kubernetes memory limit.

Configuration sections​

SectionControls
[general]Listen address, authentication, TLS, metrics port, license key, shutdown timing
[storage]Data directory, buffer pool, execution memory pool
[storage.memory]Per-component memory budgets (see below)
[storage.io]I/O backend selection
[storage.wal]Write-ahead log sizing and durability
[storage.wal.archive]WAL archiving for point-in-time recovery (see Backup and restore)
[storage.wal.checkpoint]Checkpoint frequency and triggers
[storage.wal.retention]How long WAL segments are kept before reclaiming
[storage.wal.backup]Defaults used by the scramdb backup CLI
[storage.gc]Cleanup of old row versions
[storage.compaction]Background segment compaction
[execution]Query execution: workers, morsel size, join and spill tuning
[aqe]Adaptive query execution for distributed joins
[statistics]Automatic statistics collection for the planner
[optimizer]Join-ordering search limits
[gpu]GPU acceleration (see Docker: GPU acceleration)
[cluster]Multi-node clustering, Enterprise license required (see Clustering)
[udf]User-defined function runtime
[udf.thrust]Isolated worker pool for UDF calls
[udf.daemon]Autostart and supervision of long-running packages, including the Semantic AI MCP server on port 9191
[udf.registry]Package registry policy: allowed registries, tokens, offline mode

Every field has a default, so an omitted section (or an omitted field) behaves exactly as it would out of the box.

General settings​

[general]
pg_address = "127.0.0.1:5432" # engine default is loopback-only; the published
# Docker image's startup command overrides this to
# 0.0.0.0:5432
metrics_port = 9090 # Prometheus /metrics and plain /health (0 disables)
jit_enabled = true # compile hot queries to native code

pg_no_auth = false # true trusts every connection with no password
# check and ignores hba_file entirely
hba_file = "" # pg_hba.conf-style rules; empty uses the built-in
# default (localhost trusted, password elsewhere)
tls_cert = "" # PEM certificate path; empty disables TLS
tls_key = "" # PEM key path; empty disables TLS

license_key = "" # Enterprise/Trial license; also settable with
# SCRAMDB_LICENSE_KEY, which wins if both are set

shutdown_drain_timeout_secs = 10 # time to let active queries finish on shutdown
shutdown_jit_quiesce_timeout_secs = 10 # time to let the JIT background worker settle

pg_no_auth, hba_file, tls_cert, and tls_key are TOML-only: there is no CLI flag or environment variable that turns authentication or TLS on. --pg-no-auth on the command line is equivalent to setting pg_no_auth = true. See Docker: Securing a node for the security implications of the shipped image's defaults.

shutdown_drain_timeout_secs and shutdown_jit_quiesce_timeout_secs bound how long a graceful shutdown can take. If you run under docker stop -t <seconds> or a Kubernetes terminationGracePeriodSeconds, set that grace period to at least the sum of both so ScramDB finishes its own drain instead of being killed mid-shutdown.

Memory​

ScramDB manages memory as a set of per-component budgets under [storage.memory]. Sizes are byte strings like "64MB".

[storage.memory]
per_operator_bytes = "64MB" # per sort or hash operator (like work_mem)
maintenance_bytes = "256MB" # headroom for ANALYZE and CREATE INDEX
working_set_bytes = 0 # 0 = auto-size from working_set_percent
working_set_percent = 75 # target this share of available RAM
read_buffer_pool_bytes = "256MB" # cap on scan and COPY page traffic
effective_cache_bytes = 0 # 0 = auto-size from effective_cache_percent
effective_cache_percent = 50 # planner hint: how much data stays cached

Set a *_bytes value to pin it exactly, or leave it at 0 to let ScramDB size it from the matching *_percent of detected RAM.

Execution​

[execution]
workers = "auto" # "auto" uses every core, or set an explicit count
morsel_size = "4MB" # unit of parallel work handed to each worker
transactional_morsel_threshold = 4 # <= this many morsels (or any DML) gets priority dispatch
aging_promotion_ms = 10 # analytical starvation ceiling, in milliseconds
jit_prefetch_distance = 8 # rows ahead compiled kernels prefetch hash buckets
jit_compile_threads = 3 # background compile pool size

workers = "auto" shares every thread in the resolved worker pool across a query's morsel dispatch, and an explicit number caps how many of those threads one query uses. How many threads the pool itself has depends on the container or OS CPU limit, not just the visible core count; see Parallelism for exactly how it's resolved.

KeyTypeDefaultDescription
transactional_morsel_thresholdinteger, 1..=644A statement dispatching this many morsels or fewer, or any DML statement, classifies as transactional and rides the scheduler's priority lane, dequeued ahead of analytical work. An out-of-range value fails at startup with the accepted range in the error.
aging_promotion_msinteger (milliseconds), 1..=100010The starvation ceiling: analytical work waiting longer than this is served ahead of further transactional work, a hard latency bound under sustained short-statement pressure. Same startup validation as above.
jit_prefetch_distanceinteger8Rows ahead the compiled kernels software-prefetch hash-table buckets. The effective distance is further clamped by a fill-buffer budget, so raising it past what a multi-stream kernel can sustain changes nothing rather than stalling the core. 0 disables prefetch emission. Applies to kernels compiled after the change.
jit_compile_threadsinteger, clamped to 1..=83Background compile pool size. At the default, different queries' tier ladders compile in parallel instead of queueing behind one worker, which keeps a newly seen query shape from running interpreted while a backlog drains. Each worker holds a 16 MiB stack and compiles are CPU-bound, so a large pool competes with query workers. Set 1 for strict single-threaded compilation.

See Mixed transactional and analytical load for how this scheduling actually behaves.

I/O backend​

[storage.io]
backend = "auto" # resolves to direct I/O (bypasses the OS page cache) today

"auto" is the always-safe choice and is what the shipped image uses. See Limitations for backends that are opt-in or still planned.

Write-ahead log and point-in-time recovery​

The write-ahead log (WAL) is what makes crash recovery and point-in-time recovery (PITR) possible. Archiving is on by default in the shipped image:

[storage.wal.archive]
enabled = true # on by default in the shipped image, so PITR
# works out of the box
destination = "" # empty = "{data_dir}/wal-archive"; set an explicit
# object_store URL (s3://, gs://, az://, file://)
# to archive off-box

In a cluster, leave destination unset only if you're comfortable with per-node archives: an unset destination fragments PITR history across leader failovers, because each node archives to its own local subdirectory. Point destination at a shared location (typically an object store URL) for a cluster where you want one continuous PITR history regardless of which node led at a given time.

[storage.wal.checkpoint], [storage.wal.retention], and [storage.wal.backup] tune checkpoint frequency, how long WAL segments are retained, and the defaults the scramdb backup CLI uses; every field has a default and none of them need to be set to get archiving and recovery working.

Backup and restore (PITR)​

scramdb ships two subcommands for point-in-time backup and restore, alongside the normal server command:

# Take a full backup to a local directory or an object store URL
scramdb backup --config /etc/scramdb/config.toml --out /backups/2026-08-01

# Restore from a backup, optionally replaying WAL to a specific point
scramdb restore --config /etc/scramdb/config.toml \
--backup /backups/2026-08-01 \
--wal-source s3://my-bucket/wal-archive \
--target-time "2026-08-01T12:00:00Z"

Both --out (for backup) and --backup / --wal-source (for restore) accept a local directory path or an object_store URL: s3://, gs://, az://, or file://. restore accepts either --target-time <RFC3339 timestamp> or --target-lsn <n> to stop replay at a specific point; omit both to replay to the end of the available WAL.

Because [storage.wal.archive] is enabled by default in the shipped image, a fresh backup plus the archived WAL is enough to restore to any point after the backup was taken, with no extra setup.

Environment variables​

A few settings come from the environment rather than the config file; see Environment Variables for the canonical table and defaults. Cluster nodes in the Docker image also read NODE_NAME, ADVERTISE_ADDR, SEEDS, and CONFIG_PATH, consumed by the container's entrypoint script rather than the engine; see Clustering.

Command-line flags​

FlagPurpose
-c, --config <file>Path to the TOML config file (default scramdb-config.toml)
--pg-address <host:port>PostgreSQL wire-protocol listen address
--pg-no-authTrust every connection with no password check (development only)
--metrics-port <port>Prometheus metrics port (0 disables it)

scramdb backup and scramdb restore are separate subcommands with their own flags; see Backup and restore above.