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:

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.