Skip to content
PostRESP
Esc
↑↓navigate↵open⌘Jpreview
On this page

Authentication

Redis-style AUTH backed by PostgreSQL LOGIN roles.

PostRESP accepts Redis-style AUTH and validates credentials against PostgreSQL LOGIN roles. Wire auth is off by default (like Redis with no requirepass).

Warning — Docker defaults: The image listens on 0.0.0.0:6379 with wire auth off. Publishing -p 6379:6379 exposes an unauthenticated RESP store (same stance as the official Redis image). If you do not set POSTRESP_PASSWORD, the entrypoint generates a random secret on first start, prints it once to the container log, and persists it on the data volume — use that value when you later enable RequireClientAuth. Standalone / JSON still defaults to Password: postresp; the gateway warns if wire auth is on with that built-in secret.

Credentials

Role Purpose Used by gateway? Defaults
postresp RESP default user Yes Username postresp. Docker: random POSTRESP_PASSWORD if unset (see below). JSON / standalone: Password: postresp
Other LOGIN roles Per-app RESP users Yes — AUTH user pass created with pgresp.create_user
postgres SQL admin (psql, migrations) No POSTGRES_USER / POSTGRES_PASSWORD

The gateway never connects as the SQL superuser.

Docker: generated secret when unset

Priority for the RESP wire secret:

  1. POSTRESP_PASSWORD / VALKEY_PASSWORD / REDIS_PASSWORD
  2. *_PASSWORD_FILE
  3. Persisted file /var/lib/postgresql/.postresp_password (from a previous start)
  4. Generate a random 32-byte secret, write that file (chmod 600), print it once to stderr, then continue

Wire auth stays off until you enable it; the secret is still used for the postresp Postgres LOGIN role. Override with an explicit env var anytime (then pgresp.set_password if the role already exists with a different secret).

Password name map (easy to mix up)

Name Layer Meaning
POSTRESP_PASSWORD env (preferred) RESP default-user wire secret
VALKEY_PASSWORD / REDIS_PASSWORD env fallbacks Same wire secret if POSTRESP_PASSWORD unset
Password SetupConfiguration.json Same wire secret
Username SetupConfiguration.json RESP default role (POSTRESP_USERNAME, default postresp)
POSTGRES_PASSWORD official Postgres image SQL superuser only — not read by the gateway

On first init, if POSTGRES_PASSWORD is unset, the entrypoint may seed it from the RESP secret so psql as postgres works. That copy is one-way; the gateway still only reads POSTRESP_PASSWORD / Password.

Default database is postgres (official image default when POSTGRES_DB is unset). Keys live in schema pgresp.

AUTH

Redis ACL-style usernames: omit the user, or use the literal name default, to mean the configured default role (POSTRESP_USERNAME, default postresp).

Form Meaning
AUTH <password> Password-only (requirepass); authenticates as the default role
AUTH default <password> Same default role (default is an alias, not a Postgres role name)
AUTH postresp <password> Explicit default role (when POSTRESP_USERNAME is postresp)
AUTH app <password> Extra LOGIN role
# Prefer env over -a (avoids password in `ps` / shell history)
export REDISCLI_AUTH="$POSTRESP_PASSWORD"
redis-cli -p 6379 PING

redis-cli -p 6379
AUTH "$POSTRESP_PASSWORD"
AUTH default "$POSTRESP_PASSWORD"
AUTH postresp "$POSTRESP_PASSWORD"
# Empty-user URLs work with many clients; some Redis 6+ libs prefer `default`:
REDIS_URL=redis://:${POSTRESP_PASSWORD}@127.0.0.1:6379
REDIS_URL=redis://default:${POSTRESP_PASSWORD}@127.0.0.1:6379
REDIS_URL=redis://postresp:${POSTRESP_PASSWORD}@127.0.0.1:6379

When wire auth is on, unauthenticated commands return -NOAUTH Authentication required. Wrong credentials return WRONGPASS invalid username-password pair.

When wire auth is off, clients need not AUTH, but AUTH still validates credentials (wrong password → WRONGPASS). That differs from classic Redis “no password configured” rejecting AUTH outright.

Postgres backends for the default role are pooled (deadpool, one LOGIN). PING and other protocol-only commands do not check out a backend. Extra AUTH user pass roles still open a dedicated session — a pool is one Postgres LOGIN, so those users cannot share the default-role pool.

Enabling wire auth

RequireClientAuth defaults to false. Setting a password alone does not require AUTH. Enable wire auth with either:

JSON (preferred in config files):

{
  "RequireClientAuth": true,
  "Password": "long-random-secret"
}
POSTRESP_PASSWORD=long-random-secret

Env alone (Redis/Valkey image compat):

ALLOW_EMPTY_PASSWORD=no
POSTRESP_PASSWORD=long-random-secret

ALLOW_EMPTY_PASSWORD=no forces wire auth on at startup even when JSON has RequireClientAuth: false. Always set a non-default POSTRESP_PASSWORD when auth is on.

Auth knobs — which wins?

Applied at config load (env overrides JSON), then at runtime via CONFIG SET:

Source Effect
RequireClientAuth in JSON Startup “must AUTH” when ALLOW_EMPTY_PASSWORD is unset (false by default)
ALLOW_EMPTY_PASSWORD=yes Forces wire auth off, even if JSON has RequireClientAuth: true
ALLOW_EMPTY_PASSWORD=no Forces wire auth on, even if JSON has RequireClientAuth: false
CONFIG SET requirepass <secret> Wire auth on; updates in-process secret + default role’s Postgres LOGIN
CONFIG SET requirepass "" Wire auth off for this process; LOGIN + memory revert to the startup secret — not “no password”

CONFIG SET requirepass does not rewrite env / JSON. After restart, startup config wins again.

Example footgun:

  1. Cold start with RequireClientAuth: true, Password: startup-secret.
  2. CONFIG SET requirepass rotated-secret — clients must use rotated-secret.
  3. CONFIG SET requirepass "" — wire auth off; role password is startup-secret again (not empty). Anyone who still knows startup-secret can authenticate once auth is turned back on.
  4. Process restart with the same JSON — auth is on again with startup-secret (the "" clear is gone). Persist rotations by updating POSTRESP_PASSWORD / Password (and pgresp.set_password) before restart.

Bind address (Docker = Redis image tradeoff)

The Docker image listens on all interfaces (UseLocalHost: false → 0.0.0.0), with wire auth off and no protected mode. Same stance as the official Redis image: bind wide open inside the container; let Docker networking be the firewall. Exposure is controlled by whether you publish the port (-p 6379:6379) or only attach an internal network.

If you do publish to an internet-facing host, set a password and turn on wire auth.

Optional escape hatch: POSTRESP_ALLOW_REMOTE_CONNECTIONS=false binds loopback only (breaks published-port / cross-container access the same way a custom bind 127.0.0.1 redis.conf does). The Rust library default is also loopback-only — for standalone make run, not for the image.

How AUTH checks passwords (pg_hba)

After first init, the image’s pg_hba.conf is effectively:

local   all   postgres                   trust
local   all   all                        scram-sha-256
host    all   all   127.0.0.1/32         trust    # from official image / initdb
host    all   all   ::1/128              trust
host    all   all   all                  scram-sha-256   # remote TCP

Init script docker/init-pg-hba-scram.sh (installed as /docker-entrypoint-initdb.d/00-pg-hba-scram.sh) only rewrites the blanket local all all trust line. Host / TCP lines are left as the official image wrote them (loopback trust, everything else scram-sha-256).

The in-process gateway dials Postgres on the unix socket (PostgresHostName: /var/run/postgresql):

  1. Default-user AUTH — compared in-process to POSTRESP_PASSWORD / Password, then the gateway opens a session as that role.
  2. Extra-user AUTH — Postgres verifies the LOGIN password over the socket (local … scram-sha-256).

Local SQL admin threat model

Inside the container, local … postgres trust and loopback host … trust mean any process that can reach the socket or 127.0.0.1:5432 is SQL superuser without a password. That matches the official image’s localhost behavior; the socket is not shared to the host unless you bind-mount it. Remote TCP still requires POSTGRES_PASSWORD (catch-all scram).

Prefer socket admin from inside the container:

docker exec -i postresp psql -U postgres -d postgres

Additional RESP users

docker exec -i postresp psql -U postgres -d postgres -c \
  "SELECT pgresp.create_user('app', 'app-secret');"
docker exec -i postresp psql -U postgres -d postgres -c \
  "GRANT USAGE ON SCHEMA pgresp TO app;"

pgresp.create_user / pgresp.set_password take the plain wire secret and encode it for Postgres LOGIN internally — do not pass base64 yourself.

redis-cli -p 6379
AUTH app app-secret

Rotating passwords

Postgres stores role passwords in the catalog. Container env is applied on process start; POSTGRES_* init vars apply only on first init of an empty data directory. Changing env alone does not rewrite roles on an existing volume.

Default RESP role

  1. Choose a new long random secret.
  2. Update Postgres (as postgres):
docker exec -i postresp psql -U postgres -d postgres -c \
  "SELECT pgresp.set_password('postresp', 'new-secret');"
  1. Set POSTRESP_PASSWORD=new-secret (and Password if you manage JSON) and restart the gateway.
  2. Update clients.

Or from an authenticated RESP session: CONFIG SET requirepass new-secret (still update POSTRESP_PASSWORD for the next cold start).

Extra RESP users

docker exec -i postresp psql -U postgres -d postgres -c \
  "SELECT pgresp.set_password('app', 'new-app-secret');"

No gateway restart required; update those clients.

SQL superuser

docker exec -i postresp psql -U postgres -d postgres -c \
  "ALTER ROLE postgres PASSWORD 'new-sql-secret';"

The gateway does not use this password. For remote psql -h …, set PGPASSWORD / .pgpass to match; loopback inside the container remains trust.

Redis passwords are binary-safe; Postgres role passwords are not. For each RESP wire secret, PostRESP sets the role’s LOGIN password string to standard Base64 (RFC 4648 §4: A–Za–z0–9+/ with = padding — not base64url). Postgres then stores a SCRAM verifier of that string — not the base64 plaintext. Clients and env always use the plain wire secret (gateway path supports NULs / non-UTF-8; SQL helpers pgresp.create_user / pgresp.set_password take UTF-8 text and encode internally).

Redis requirepass / CONFIG SET map

Redis PostRESP
requirepass secret Default role secret + wire auth on
AUTH secret / -a Default role + that secret
AUTH default secret default → POSTRESP_USERNAME (default postresp)
CONFIG SET requirepass secret Updates default role LOGIN + in-process secret; wire auth on
CONFIG SET requirepass "" Wire auth off; reverts LOGIN + memory to the startup secret (not empty)

RESET

Matches Redis RESET (6.2+): clears subscription state, client name, and authentication. With wire auth on, the connection must AUTH again.

Not implemented

  • Redis ACL command family (ACL SETUSER, …)
  • Sentinel / Cluster auth APIs
  • TLS termination inside the gateway

Was this page helpful?