Quick Start
Get ScramDB running in 2 minutes with Docker, or natively on Linux with the one-line installer, then grow it into a three-node cluster.
1. Pull and Run
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
The three ports: 5432 is the PostgreSQL wire protocol, 9090 serves Prometheus metrics,
and 9191 is the Semantic AI MCP endpoint for AI agents (see
Semantic AI).
The -v scramdb-data:/var/lib/scramdb mount keeps your data across docker stop / docker start and even a docker rm of the container, since it lives in a named Docker volume instead of the container's writable layer.
The published image's default startup command runs with authentication disabled (--pg-no-auth), listening on all interfaces. Anyone who can reach port 5432 can connect with any username, no password. This is intentional: it makes local development frictionless. It is not safe to expose this container to an untrusted network as-is.
To require passwords, override the container's default 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 \
scramdb/scramdb:latest \
--pg-address 0.0.0.0:5432
With authentication enabled, connections from localhost are still trusted without a password; connections from anywhere else must authenticate with SCRAM-SHA-256. The bootstrap scramdb role has no password set until you give it one:
ALTER ROLE scramdb WITH PASSWORD 'a-real-password';
Alternative: local install without Docker (Linux)
Prefer a native binary? The installer downloads the latest build, verifies its
checksum, installs everything into ~/.scramdb (binary, config, and the
curated UDF packages), and puts scramdb on your PATH. It never touches an
existing data directory or config.
curl -fsSL https://scramdb.com/install | bash
Then open a new terminal and start the server:
scramdb
That's it: it finds its config automatically, listens on 127.0.0.1:5432, and
runs in the background (log: ~/.scramdb/scramdb.log). Manage it with
scramdb status and scramdb stop. Connections from localhost are trusted
without a password, so step 2 below works as-is.
Requirements: Linux x86-64 with AVX2 (Intel Haswell 2013 / AMD Zen 1 2017 or newer) and glibc 2.36+, or Linux arm64 (Neoverse-N1 baseline, e.g. AWS Graviton2+). The installer checks these up front and refuses with a clear message instead of installing a binary that would crash. On macOS there is no native build yet: use the Docker image above.
Useful variants:
curl -fsSL https://scramdb.com/install | bash -s -- --pick # choose a build/version
curl -fsSL https://scramdb.com/install | bash -s -- --no-modify-path # don't touch shell PATH config
curl -fsSL https://scramdb.com/install | bash -s -- --uninstall # remove everything, data included
curl -fsSL https://scramdb.com/install | bash -s -- --help # all options
2. Connect
Use psql or any PostgreSQL client:
psql -h localhost -p 5432 -U scramdb scramdb
You should see:
scramdb=#
3. Create a Table
CREATE TABLE sensors (
id BIGINT PRIMARY KEY,
device_id INTEGER NOT NULL,
temperature DOUBLE PRECISION,
humidity DOUBLE PRECISION,
recorded_at TIMESTAMP
);
4. Insert Data
INSERT INTO sensors VALUES
(1, 101, 22.5, 45.0, '2024-03-15 10:00:00'),
(2, 101, 23.1, 44.0, '2024-03-15 10:05:00'),
(3, 102, 19.8, 55.0, '2024-03-15 10:00:00'),
(4, 102, 20.1, 54.0, '2024-03-15 10:05:00'),
(5, 103, 25.3, 38.0, '2024-03-15 10:00:00');
5. Query
-- Basic aggregation
SELECT
device_id,
AVG(temperature) AS avg_temp,
MIN(humidity) AS min_humidity,
COUNT(*) AS readings
FROM sensors
GROUP BY device_id
ORDER BY avg_temp DESC;
-- Window function
SELECT
device_id,
recorded_at,
temperature,
temperature - LAG(temperature) OVER (
PARTITION BY device_id ORDER BY recorded_at
) AS temp_change
FROM sensors;
6. Load CSV Data
COPY ... FROM '/path' reads a file on the server, inside the container, not a file on your machine. Unless you have mounted a matching volume, that path will not exist and the command will fail. For a file that lives on your own machine, use psql's \copy meta-command instead. It streams the file from the client to ScramDB, so no container-side path is needed.
First, create a CSV file on your machine:
cat > sensors.csv <<'EOF'
id,device_id,temperature,humidity,recorded_at
6,101,22.0,46.0,2024-03-15 10:10:00
7,103,24.9,37.5,2024-03-15 10:10:00
EOF
Then, from inside psql, load it:
scramdb=# \copy sensors FROM 'sensors.csv' (FORMAT CSV, HEADER)
If you do mount a volume and want the server to read the file itself, COPY works the same way psql clients expect:
-- Only works if /data/sensors.csv exists inside the container
-- (e.g. add -v $(pwd)/data:/data to your docker run command)
COPY sensors FROM '/data/sensors.csv' (FORMAT CSV, HEADER);
7. A Three-Node Cluster
The same image runs a cluster. Three environment variables per container turn cluster mode on:
NODE_NAME names the node, ADVERTISE_ADDR is the address its peers dial, and SEEDS lists
its peers. Clustering needs an Enterprise or Trial license key (see
Cluster licensing for how to get one); without it each node logs why
and exits.
Stop the single node from step 1 first, so port 5432 is free:
docker stop scramdb
Start three nodes on one Docker network. They reach each other by container name on ports
7190, 7191 and 7192 inside the network, so only the client port is published:
export SCRAMDB_LICENSE_KEY="<your Enterprise or Trial key>"
docker network create scramdb-net
docker run -d --name node-a --hostname node-a --network scramdb-net \
-e NODE_NAME=node-a -e ADVERTISE_ADDR=node-a:7190 \
-e SEEDS='["node-b:7190", "node-c:7190"]' \
-e SCRAMDB_LICENSE_KEY \
-p 5432:5432 -v node-a-data:/var/lib/scramdb \
--stop-timeout 120 scramdb/scramdb:latest
docker run -d --name node-b --hostname node-b --network scramdb-net \
-e NODE_NAME=node-b -e ADVERTISE_ADDR=node-b:7190 \
-e SEEDS='["node-a:7190", "node-c:7190"]' \
-e SCRAMDB_LICENSE_KEY \
-p 5433:5432 -v node-b-data:/var/lib/scramdb \
--stop-timeout 120 scramdb/scramdb:latest
docker run -d --name node-c --hostname node-c --network scramdb-net \
-e NODE_NAME=node-c -e ADVERTISE_ADDR=node-c:7190 \
-e SEEDS='["node-a:7190", "node-b:7190"]' \
-e SCRAMDB_LICENSE_KEY \
-p 5434:5432 -v node-c-data:/var/lib/scramdb \
--stop-timeout 120 scramdb/scramdb:latest
-e SCRAMDB_LICENSE_KEY passes the key you exported into each container. --stop-timeout 120
gives a node the time it needs on docker stop to hand its data and its vote back to the others.
A node accepts clients once the cluster has formed, which it waits up to a minute for, so start
all three together. Watch them turn healthy:
docker ps --filter name=node- --format "{{.Names}}: {{.Status}}"
Connect to any node and ask the cluster about itself:
psql -h localhost -p 5432 -U scramdb scramdb
SELECT node_id, role, state FROM scram.nodes ORDER BY node_id;
node_id | role | state
---------+-------+-------
node-a | voter | live
node-b | voter | live
node-c | voter | live
(3 rows)
Create a distributed table. On a cluster, every table with a primary key is distributed with no extra syntax: its rows are placed in buckets by a hash of the key, and each bucket is kept on three nodes (the replication factor), so the cluster keeps serving with one node down.
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL,
total NUMERIC(12, 2) NOT NULL
);
INSERT INTO orders
SELECT t.g, t.g % 100, t.g * 1.25
FROM generate_series(1, 10000) AS t(g);
SELECT count(*), sum(total) FROM orders;
count | sum
-------+-------------
10000 | 62506250.00
(1 row)
Every node serves the same table. Ask node-b, and see where the buckets live:
psql -h localhost -p 5433 -U scramdb scramdb -c "SELECT count(*) FROM orders"
psql -h localhost -p 5433 -U scramdb scramdb -c "SHOW SHARDS"
Remove the cluster and bring the single node back:
docker rm -f node-a node-b node-c
docker volume rm node-a-data node-b-data node-c-data
docker network rm scramdb-net
docker start scramdb
The engine repository's docker-compose.yml runs the same three nodes with one command. Next:
Forming a cluster for Compose, bare metal and Kubernetes,
Distributed Cluster for what a cluster guarantees and how to deploy
it, and Ports for every port a node opens.
What's Next?
- Forming a Cluster - Grow the cluster above into a real deployment
- First Queries - More SQL examples with joins, subqueries, and session settings
- SQL Reference - Full SQL compatibility reference
- Data Types - PostgreSQL type mapping
- Clients and Drivers - Connect from your language's standard PostgreSQL driver