Start the server

One binary serves either storage backend, chosen by the --db URL.

# Local file backend on port 5433
cargo run -p twill-server -- --listen 127.0.0.1:5433 --db file://./srv.db

# Or disaggregated on object storage
cargo run -p twill-server -- --listen 0.0.0.0:5433 --db s3://my-bucket/mydb

The container image (ghcr.io/bihaviour/twill-db) runs the same binary for self-host / managed deployments.

Metrics & health (self-hosted)

Pass --metrics HOST:PORT to expose an operator-facing /metrics endpoint in Prometheus text format, plus a /healthz liveness probe, on a separate address from the Postgres-wire listener. It is off by default and self-hosted — the server never sends anything outbound, so there is no telemetry to opt out of.

# Wire on 5433, metrics on 9100 (scrape http://host:9100/metrics)
cargo run -p twill-server -- --listen 0.0.0.0:5433 --db file://./srv.db --metrics 0.0.0.0:9100

curl -s http://127.0.0.1:9100/metrics   # Prometheus exposition
curl -s http://127.0.0.1:9100/healthz   # -> ok

Two families are exported. Wire-level counters come from the listener: twilldb_connections_total / twilldb_connections_active, twilldb_queries_total, twilldb_query_errors_total, twilldb_rows_returned_total, and twilldb_uptime_seconds. Engine / storage gauges are read live from the engine: twilldb_engine_commits_total, twilldb_engine_committed_lsn, twilldb_engine_write_acquires_total / …_write_handoffs_total (the serialized-handoff signal — see hot-row contention), and the storage seam's twilldb_storage_wal_appends_total / …_wal_bytes_total / …_cache_hits_total / …_cache_misses_total / …_fsyncs_total. Point Prometheus at the endpoint and graph error rate (rate(twilldb_query_errors_total[5m])), throughput, and cache hit ratio in Grafana.

A scrape is the same cost as one client connecting; 15s is a sane interval. The same SHOW twill.stats surface is available in-band over the wire if you'd rather pull the engine gauges through a SQL client.

A ready-to-run Prometheus + Grafana stack (with an auto-provisioned dashboard) lives in packaging/observability/docker compose up and open Grafana at localhost:3000. See its README.md for the metric reference.

The connection string

Every client uses an ordinary Postgres DSN. The database name is accepted but nominal — the server serves the database given by --db.

postgresql://[email protected]:5433/main?sslmode=disable

Always sslmode=disable

The listener speaks Protocol 3.0 startup with trust auth over cleartext. TLS termination and SCRAM auth are deliberate non-goals for now — terminate TLS at a proxy if you need it on an untrusted network. Connect with sslmode=disable (or the driver's equivalent) everywhere.

The one rule: connected ≠ every query runs

Twill implements both the simple and the full extended query protocol (Parse / Bind / Describe / Execute), which is exactly what prepared-statement clients and ORMs use — so they connect with no special driver. But the engine's SQL surface is a focused (growing) subset of Postgres. The rule of thumb:

  • Connect The wire protocol gets any Postgres tool talking to the engine.
  • Run Whether a given query executes depends on the supported SQL subset. Anything unsupported returns a clean ENGINE_ERR_SQL — reject, never mis-parse.
  • Schema Runtime CRUD generally maps cleanly; ORM migration/introspection tooling that probes full Postgres catalog behaviour is the rough edge. Prefer authoring schema with plain validated SQL.

Pick your client

Connections & scale

The engine is single-writer with one OS thread per connection. For serverless or many-client workloads, put a transaction-mode pooler (PgBouncer / pgcat) in front to absorb connection bursts — see Connection pooling. The storage backend (and thus scale-to-zero) is independent of the client — see Storage backends.

Twill DB documentation · Licensed under BUSL-1.1. · Author