Configure
Configuration
Settings come from a TOML file, from STREAMQL_* environment variables and from command-line flags.
Sources and precedence#
StreamQL starts with no configuration at all when you pass --upstream (or STREAMQL_UPSTREAM__URL). More settings come from layers, each overriding the one before:
- Built-in defaults.
- A TOML file given with
--configorSTREAMQL_CONFIG. - Environment variables named
STREAMQL_<SECTION>__<KEY>, with a double underscore between nesting levels. - Command-line flags such as
--listen,--pool-modeand--pool-size.
This is the twelve-factor layout: the same image runs everywhere and only the environment differs. Unknown keys are rejected, so a typo fails at start-up instead of being ignored.
STREAMQL_UPSTREAM__URL=file:/run/secrets/upstream_url
STREAMQL_ADMIN__TOKEN=file:/run/secrets/admin_token
STREAMQL_DEFAULTS__MODE=transaction
STREAMQL_DEFAULTS__MAX_SIZE=20
STREAMQL_LOG__FORMAT=jsonValidate and reload#
streamql config validate checks the effective configuration and exits non-zero on errors. streamql config show prints it as TOML. A running node reloads on SIGHUP or PUT /api/v1/config, and every change is tagged with how it takes effect:
| Effect | Meaning | Examples |
|---|---|---|
live | Applied atomically to new connections and acquisitions | Most pool, limit and policy settings |
needs_drain | Changes a listener or open pools; takes effect after a drain or a binary handoff | listen.address, upstream.url, defaults.mode, pool[*].database, pool[*].mode |
needs_restart | Fixed for the life of the process | admin.enabled, admin.address, admin.state_file, runtime.worker_threads, runtime.handoff_socket, log.format |
The console shows the effect of every field and keeps a versioned history. Nothing is applied silently.
Secrets#
Secret settings accept a reference instead of a value, so the file and the environment can stay free of secrets: env:, file:, systemd:, k8s:, vault:, vault-logical:, aws-sm:, azure-kv: and gcp-sm:. A plain value is accepted too, and literal: forces one. References are resolved into an encrypted in-process vault, watched, and re-read every secrets.refresh_s seconds (default 60); a rotation reloads the configuration. StreamQL refuses to run as root unless secrets.allow_root is set.
listen#
| Key | Default | Meaning |
|---|---|---|
listen.address | 0.0.0.0:6432 | Client listen address |
listen.tls.cert, listen.tls.key | unset | Certificate and private key for client TLS |
listen.require_tls | false | Reject clients that do not negotiate TLS |
listen.max_client_connections | 10000 | Client connection ceiling |
admin#
| Key | Default | Meaning |
|---|---|---|
admin.enabled | true | Serve the admin API and console |
admin.address | 127.0.0.1:9930 | Admin API and console address |
admin.token | unset | Bearer token for mutating endpoints (a secret reference) |
admin.require_token_for_reads | false | Require the token for read endpoints too |
admin.state_file | unset | Where changes made through the API persist across restarts |
Without admin.state_file, databases and settings changed in the console last only until the next restart, and the Databases screen says so.
upstream#
| Key | Default | Meaning |
|---|---|---|
upstream.url | unset | postgres://user[:password]@host:port/database?sslmode=..., or a secret reference to one |
upstream.password | unset | Password of the URL user (a secret reference) |
upstream.host, upstream.port | 127.0.0.1, 5432 | Server address when no URL is given |
upstream.sslmode | prefer | disable, prefer, require, verify-ca or verify-full |
upstream.sslrootcert | unset | PEM CA bundle for verify-ca and verify-full; the WebPKI roots when unset |
upstream.connect_timeout_ms | 5000 | Server connection timeout |
upstream.readiness_check | true | /readyz reports not ready while the upstream is unreachable |
upstream.readiness_grace_s | 5 | How long the upstream may be unreachable before /readyz fails |
auth#
Client login uses auth.method: scram-sha-256 (default), md5 or trust. Use trust only for development; it accepts any client without a password.
| Key | Default | Meaning |
|---|---|---|
auth.method | scram-sha-256 | Client authentication method |
auth.file | unset | File of "user" "secret" lines; create a verifier with streamql scram-verifier |
auth.users | empty | Inline users, each with name and secret (a password, an md5 hash or a SCRAM-SHA-256$ verifier, or a reference) |
auth.auth_query | unset | Look up secrets of users missing from the file on the server |
With auth_query, the lookup role defaults to streamql_auth, results are cached for cache_ttl_s (default 60), and the default query reads pg_shadow for non-superusers that are not expired. Keys: user, password, database, query, cache_ttl_s.
defaults and pools#
[defaults] applies to every pool. A [[pool]] entry overrides it for one database.
| Key | Default | Meaning |
|---|---|---|
defaults.mode | transaction | session, transaction or statement |
defaults.max_size | 20 | Server connections per database and role |
defaults.min_idle | 0 | Idle server connections kept open |
defaults.queue_timeout_ms | 5000 | How long a client waits for a server connection before error 53000 |
defaults.server_lifetime_s | 3600 | Maximum age of a server connection |
defaults.server_idle_timeout_s | 600 | Idle server connections are closed after this |
defaults.server_check_delay_s | 30 | Health-check a backend on checkout after it has been idle this long |
defaults.statements_per_backend | 500 | Prepared statements kept per server connection |
defaults.auto_create | true | Create pools on demand for databases without a [[pool]] entry |
defaults.max_pinned_fraction | 0.9 | Maximum fraction of a pool's backends that may be pinned |
defaults.max_db_connections | 0 | Server connections to one database across all users; 0 is unlimited |
defaults.max_db_clients | 0 | Client connections to one database; 0 is unlimited |
defaults.reserve | size 0, after_wait_ms 1000, max_slowdown 2.0 | Extra server connections opened only after a client waited |
defaults.tenant | unset | Per-tenant share, with key and share |
[defaults]
mode = "transaction"
max_size = 20
[[pool]]
database = "shop" # name clients connect to
upstream_database = "shop_v2" # name on the server; defaults to database
user = "app" # log in to the server as this role
mode = "session"
max_size = 40
queue_timeout_ms = 2000
[pool.reserve]
size = 5
after_wait_ms = 1000A [[pool]] entry accepts: database, upstream_database, host, port, user, mode, max_size, min_idle, queue_timeout_ms, max_db_connections, max_db_clients, reserve and tenant.
Pool groups#
A [[pool_group]] defines one set of settings for several databases at once: name, databases, host, port, user, mode, max_size, min_idle, queue_timeout_ms, max_db_connections, max_db_clients, reserve, tenant and max_group_connections. Each database still gets its own pool and server connections. max_group_connections caps the group as a shared limit.
session#
| Key | Default | Meaning |
|---|---|---|
session.pin_unknown_functions | true | Pin a session that calls a function outside the built-in allowlist |
session.attested_functions | empty | Functions you declare free of session effects |
budget and limits#
| Key | Default | Meaning |
|---|---|---|
budget.max_connections | 0 | Upper bound on backend connections this member may hold; 0 probes the server |
budget.reserved, budget.outside_connections | 0, 0 | Connections kept back, and connections that come from outside StreamQL |
limits.max_message_bytes | 268435456 | Largest message held whole in memory |
limits.stream_large_messages | true | Relay oversized row and COPY data in pieces instead of rejecting it |
limits.client_idle_timeout_s | 0 | Idle client timeout; 0 disables |
limits.auth_timeout_s | 30 | Time allowed to finish authentication |
limits.max_user_connections | 0 | Server connections of one backend role across all databases |
limits.max_user_clients | 0 | Client connections of one user; error 53300 at login |
limits.max_unauthenticated | 1024 | Connections that have not finished authentication |
limits.failed_login | max_failures 20, window_s 60, block_s 60 | Block an address after repeated failed logins; 0 failures disables |
runtime, drain and log#
| Key | Default | Meaning |
|---|---|---|
runtime.worker_threads | 0 | Worker threads; 0 is one per CPU |
runtime.tcp_nodelay, runtime.keepalive_s | unset | Socket options |
runtime.handoff_socket | unset | Unix socket on which this process hands its listen sockets to a successor binary |
drain.endpoint_propagation_ms | 2000 | Keep accepting for this long after readiness goes false |
drain.grace_s | 30 | Time open transactions get to finish |
log.level | info | Level or filter, for example streamql_proxy=debug |
log.format | text | text or json |
dataplane#
| Key | Default | Meaning |
|---|---|---|
dataplane.engine | tokio | tokio or reactor (opt-in) |
dataplane.threading | auto | auto, work_stealing or per_core |
dataplane.release_idle_buffers | true | Release buffers of idle sessions |
dataplane.defer_begin | false | Defer BEGIN until the first statement; changes transaction_timestamp() |
dataplane.pool_handoff | direct | direct or queue |
dataplane.relay | copy | copy or passthrough |
dataplane.clock | coarse | coarse or precise |
dataplane.max_read_chunk | 16384 | Bytes read per call |
dataplane.steal_after_ms | 5 | How long a waiter waits before work is stolen |
dataplane.backpressure_bytes | 67108864 | Pause reading from a side whose opposite output is over this |
dataplane.evidence | true | Record one evidence record per statement for Decisions and Trace |
dataplane.evidence_capacity | 8192 | Records kept in the in-memory ring |
Every option here is an independent kill switch: set it back to its default to revert that behaviour.
insights#
See Insights and X-ray for [insights], and MCP access for [mcp]. Fleet settings are in Cluster, access and sign-in settings in Security.