Skip to main content

Docker Deployment

ScramDB ships as a single Docker image that runs both single-node and cluster deployments. This page covers running one node. For a multi-node cluster, see Clustering (requires an Enterprise license).

Quick Start​

docker pull scramdb/scramdb:latest

docker run -d \
--name scramdb \
-p 5432:5432 \
-p 9090:9090 \
-p 9191:9191 \
-v scramdb-data:/var/lib/scramdb \
scramdb/scramdb:latest

Connect with any PostgreSQL client:

psql "host=127.0.0.1 port=5432 user=scramdb dbname=scramdb"

Port 5432 is the PostgreSQL wire protocol port, 9090 serves Prometheus metrics at /metrics and a plain health check at /health, and 9191 is the Semantic AI MCP server. The image also exposes the cluster transport's ports, 7190 to 7192, which only a cluster node opens; see Ports for all of them.

Securing a node​

The published image's container command is --pg-address 0.0.0.0:5432 --pg-no-auth. That last flag trusts every incoming connection with no password check, and it also disables hba_file-based rules. This is intentional for a fast local start, but it means the container listens on every interface with no authentication until you change it.

The engine itself does not default this way: with no flags and no config file, ScramDB binds to 127.0.0.1:5432 only and authentication is on. The 0.0.0.0 bind and the disabled auth both come from the image's own startup command, not from the engine.

To run with authentication in Docker, override the container command and drop --pg-no-auth:

docker run -d \
--name scramdb \
-p 5432:5432 \
-p 9090:9090 \
-p 9191:9191 \
-v scramdb-data:/var/lib/scramdb \
-v $(pwd)/scramdb.toml:/etc/scramdb/config.toml:ro \
scramdb/scramdb:latest \
--config /etc/scramdb/config.toml --pg-address 0.0.0.0:5432

A command that names a config file with --config (or -c) runs the server with that file; one that names none runs it with the image's /etc/scramdb/config.toml, the path the command above mounts over.

With --pg-no-auth absent, ScramDB uses its built-in rules by default: connections from localhost are trusted, everything else requires a password. To use your own rules, point hba_file at a mounted pg_hba.conf-style file. To add TLS, set tls_cert and tls_key to mounted PEM files. All three keys live under [general]:

[general]
pg_address = "0.0.0.0:5432"
hba_file = "/etc/scramdb/pg_hba.conf"
tls_cert = "/etc/scramdb/server.crt"
tls_key = "/etc/scramdb/server.key"

There is no environment variable or CLI flag that turns authentication on; it is on by default and only --pg-no-auth (or pg_no_auth = true in the config) turns it off.

Configuration​

ScramDB is configured with a TOML file. The image ships a working single-node configuration, so no file is required to get started. To customize a node, mount your own configuration and point the container at it:

docker run -d \
--name scramdb \
-p 5432:5432 \
-p 9090:9090 \
-p 9191:9191 \
-v scramdb-data:/var/lib/scramdb \
-v $(pwd)/scramdb.toml:/etc/scramdb/config.toml:ro \
scramdb/scramdb:latest

A minimal configuration file:

[general]
pg_address = "0.0.0.0:5432"
metrics_port = 9090
jit_enabled = true

[storage]
prod_name = "tundra"
shard_id = 0
basedir = "/var/lib/scramdb"
buffer_pool_percent = 60
execution_memory_percent = 15

buffer_pool_percent and execution_memory_percent size the buffer pool and query execution memory as a share of the RAM ScramDB detects (see the note on container-aware sizing below). You can pin an exact size instead with buffer_pool_size_bytes = "2GB".

If you don't need to change anything under [general], mounting your own file for [storage] tuning alone is fine; every field not set falls back to its default.

See Single Node for sizing guidance and Requirements for hardware.

Environment variables​

The container reads a handful of environment variables directly; all other tuning lives in the configuration file.

VariableDefaultDescription
SCRAMDB_LOGwarnLog level: off, trace, debug, info, warn, or error. An unrecognized value silently falls back to warn.
SCRAMDB_LOG_FORMATtextSet to json for structured JSON logs
SCRAMDB_LICENSE_KEYunset (Community)Enterprise or Trial license key. Required to enable clustering; also available as license_key under [general] in the config file. The environment variable wins if both are set.
SCRAMDB_GPU_FORCEunset (false)Set to 1 or true to force GPU acceleration on

Cluster mode is selected by three additional variables (NODE_NAME, ADVERTISE_ADDR, and SEEDS), read by the container's entrypoint script rather than the engine itself, plus two optional geo-placement variables (REGION and ZONE) the same script renders straight into the node's config. They cannot be combined with a config file named by --config: the container refuses to start and says so. See Clustering, and Quick Start for three nodes started with docker run.

GPU acceleration​

[gpu] enabled = true is the shipped default, and it is safe with no GPU hardware at all: on a plain cloud VM or a laptop with no device, ScramDB detects that no GPU is present, logs one line, and falls back to CPU execution. Nothing extra needs to be mounted or added.

If a GPU is passed through to the container, ScramDB pins host memory on demand for GPU transfers. If the container's capability set already allows that (a Kubernetes pod without a capability drop, or a container that already has CAP_IPC_LOCK), nothing further is needed. If it doesn't, ScramDB automatically falls back to a pre-allocated pinned memory pool (256 MB by default, [gpu] pinned_memory_pool) at startup and keeps running: GPU queries still execute, with one extra memory copy per transfer instead of a zero-copy path.

You only need to act if you see a GPU memory-pinning error in the logs, which happens only when the pinned pool itself fails to allocate. The fix is one of:

# Option 1: raise the pinned pool
[gpu]
pinned_memory_pool = "512MB"
# Option 2: grant the capability for a zero-copy path
docker run --cap-add=IPC_LOCK ...

No other Docker capability, --privileged flag, or --shm-size setting is needed for GPU acceleration in any configuration.

Volumes​

Mount a persistent volume at /var/lib/scramdb so your data survives restarts and upgrades. It holds both the columnar storage and the write-ahead log.

docker volume create scramdb-data

docker run -d \
--name scramdb \
-p 5432:5432 \
-v scramdb-data:/var/lib/scramdb \
scramdb/scramdb:latest

The shipped image also archives the write-ahead log by default ([storage.wal.archive] enabled = true), writing to a wal-archive subdirectory inside the data volume so point-in-time recovery works out of the box with no extra setup. See Backup and restore for the CLI that reads it.

Reusing a volume from an older image

The image now runs as a pinned numeric user, UID and GID 1001:1001. An older image ran as a scramdb user allocated a system UID at build time, which was not guaranteed to be 1001 and could shift between builds. If you point this image at a named volume an older image already wrote to, the files on it may be owned by that different UID and the new image's 1001 user will fail to read or write them. Run docker run --rm --user 0 -v scramdb-data:/var/lib/scramdb scramdb/scramdb:latest chown -R 1001:1001 /var/lib/scramdb once before switching images to fix ownership.

Resource limits​

Bound the container's CPU and memory with the standard Docker flags:

docker run -d \
--name scramdb \
-p 5432:5432 \
-p 9090:9090 \
-p 9191:9191 \
-v scramdb-data:/var/lib/scramdb \
--cpuset-cpus="0-7" \
--memory=16g \
scramdb/scramdb:latest

--cpuset-cpus is preferred: an exclusive cpuset pins ScramDB to specific cores with no CFS throttling jitter, while --cpus (a quota, shown below) now also resolves to the correct worker count but shares physical cores under CFS scheduling. See Parallelism for exactly how each resolves into a worker count and for the SCRAMDB_MAX_CORES override.

docker run -d \
--name scramdb \
-p 5432:5432 \
-p 9090:9090 \
-p 9191:9191 \
-v scramdb-data:/var/lib/scramdb \
--cpus=8 \
--memory=16g \
scramdb/scramdb:latest

ScramDB reads the container's own cgroup memory limit (falling back to host memory if none is set) and sizes the buffer pool and execution memory pools from it automatically when you use the percent-based keys (buffer_pool_percent, execution_memory_percent); pinning exact byte sizes in the config file is for when you want a fixed budget regardless of the container limit.

Docker Compose​

Single node:

services:
scramdb:
image: scramdb/scramdb:latest
ports:
- "5432:5432" # PostgreSQL clients
- "9090:9090" # Prometheus metrics and health
volumes:
- scramdb-data:/var/lib/scramdb
deploy:
resources:
limits:
cpus: '8'
memory: 16G

volumes:
scramdb-data:

For a multi-node Compose file, see Clustering.

The shutdown grace period in Shutting down cleanly is a single-node figure. A cluster node also hands back its bucket ownership and its voter seat before it exits, so it needs a longer one; see Forming a cluster for the shipped multi-node Compose file and its stop_grace_period.

Health Check​

The image includes a built-in health check using pg_isready:

HEALTHCHECK --interval=10s --timeout=3s --start-period=90s --retries=3 \
CMD pg_isready -h 127.0.0.1 -p 5432 -U scramdb -d scramdb || exit 1

The 90 second start period is sized for cluster mode: in cluster mode the pgwire listener only binds after the cluster forms, which can take up to group0_bootstrap_timeout (60s by default, see [cluster]), so a shorter window would report a healthy node unhealthy while it waits for its peers. A single-node container passes as soon as its listener binds, well inside the window either way.

The metrics server also exposes a plain HTTP health endpoint, useful for a load balancer or a Kubernetes httpGet probe that shouldn't need a PostgreSQL client:

curl http://127.0.0.1:9090/health
# ok