Configuration
Stafett reads its configuration from environment variables only. There is no config file. Unknown variables are ignored, so check a name against this page rather than assuming a value took effect.
The role is not configuration: it is the image. stafett-enqueue serves the
HTTP API and stafett-deliver sends the deliveries. Every install runs at
least one of each. Either image runs setup when given it as its command.
Required
| Variable | Roles | Meaning |
|---|---|---|
DATABASE_URL |
both, and setup |
Postgres connection string, e.g. postgresql://user:password@host:5432/db. Must be a direct connection; see below. |
Signing
| Variable | Roles | Meaning |
|---|---|---|
STAFETT_SIGNING_SECRET |
deliver |
Optional. Standard Webhooks secret, whsec_ plus base64. Set, every attempt is signed and receivers verify with the same value. Unset, deliveries carry no signature headers. enqueue never needs it. |
Generate a signing secret:
echo "whsec_$(openssl rand -base64 24)"
deliver logs signing off at startup when the secret is unset. Signing is
per install: every delivery is signed, or none is. With it off you can send
your own signature headers; see the
API reference for the timestamp caveat.
Delivery
All optional. They apply to deliver.
| Variable | Default | Meaning |
|---|---|---|
STAFETT_MAX_DELIVERY_ATTEMPTS |
5 |
Attempts before a job is dead. At least 1. |
STAFETT_RETRY_INITIAL_DELAY_SECONDS |
1.0 |
Wait after the first failed attempt. |
STAFETT_RETRY_BACKOFF_MULTIPLIER |
2.0 |
Growth factor for each next wait. Greater than 1. |
STAFETT_RETRY_MAX_DELAY_SECONDS |
300.0 |
Ceiling for one wait. At least the initial delay. |
STAFETT_DELIVERY_TIMEOUT_SECONDS |
30.0 |
Longest wait for each read or write during an attempt. A destination that keeps trickling data can hold an attempt longer. |
STAFETT_DELIVERY_CONNECT_TIMEOUT_SECONDS |
5.0 |
Bound on connecting to the destination. |
STAFETT_CONCURRENCY |
10 |
Deliveries in flight per deliver process. At least 2. |
STAFETT_BLOCK_PRIVATE_URLS |
false |
Refuse destinations on private, loopback, link-local, and cloud metadata addresses. |
The wait before attempt n + 1 is
initial × multiplier^(n − 1), capped at the maximum. With the defaults that
is 1 s, 2 s, 4 s, then 8 s. A retry never runs early, but it can run up to
5 seconds late, when deliver finds it on its 5-second poll instead of
waking for it. Waits under 5 seconds often stretch to about 5 seconds.
STAFETT_CONCURRENCY is per process. Total concurrency is deliver replicas ×
STAFETT_CONCURRENCY, and one destination can receive that many requests at
once.
STAFETT_BLOCK_PRIVATE_URLS is off by default because delivering to internal
services is a normal use of a self-hosted queue. Turn it on when the URLs come
from people you do not trust. The check runs on every attempt after DNS
resolves, and a refused job is dead with reason unsafe url.
Network
| Variable | Default | Meaning |
|---|---|---|
STAFETT_HOST |
0.0.0.0 |
Address the HTTP server binds. |
STAFETT_PORT |
8000 |
Port for enqueue's API, and for deliver's /healthz. |
Outbound deliveries honour the standard HTTPS_PROXY, HTTP_PROXY,
ALL_PROXY, and NO_PROXY variables. For destinations behind a private
certificate authority, mount its certificate and point SSL_CERT_FILE (or
SSL_CERT_DIR) at it. Stafett makes no outbound calls besides deliveries.
Logs
Both roles log plain text to stderr, one line per event. Stafett never logs payloads, destination URLs, headers, or the signing secret.
The database connection
DATABASE_URL is the only database setting. deliver uses Postgres
LISTEN on it to hear about new jobs, and LISTEN only works on a session
that stays yours. Transaction poolers (PgBouncer in transaction mode, Supabase's
pooler on port 6543, and similar) hand the session to other clients and break
it. Point Stafett at the database directly.
The role in DATABASE_URL needs one of:
-
CREATEon the database, sosetupcan create schemastafett, or -
ownership of a schema
stafettthat a DBA created first:CREATE SCHEMA stafett AUTHORIZATION stafett_app;
No superuser is needed. The Helm chart and the Kubernetes example run
setup in every pod with this same connection, so the role needs those rights
at runtime too.
pgqueuer variables
Stafett sets its schema (stafett) and NOTIFY channel (stafett_ch_pgqueuer)
in code, so PGQUEUER_SCHEMA and PGQUEUER_CHANNEL do not move it, and a
pgqueuer you already run elsewhere in the same database is unaffected.
Other pgqueuer settings are still read from the environment, and Stafett does
not work with them changed. Keep PGQUEUER_PREFIX, PGQUEUER_DURABILITY,
pgqueuer's table-name variables, and QUEUE_TABLE out of Stafett's
containers.
The pgq operator tool is different: it does read them. See
Operations.