Backend
Backend configuration#
cp .env.example .envRequired#
| Variable | What it is |
|---|---|
DATABASE_URL | PostgreSQL connection string |
CRYPTO_SECRET | Encrypts stored user data — openssl rand -hex 32 |
CRYPTO_SECRET is generated once and never changed. Rotating it does not
re-encrypt anything — it makes every existing account's data unreadable.
Optional#
| Variable | Default | What it does |
|---|---|---|
PORT | 3000 | Port to listen on. |
META_NAME | My P-Stream Backend | Shown by /meta, so a client can display whose instance it's talking to. |
META_DESCRIPTION | — | Same, one line of prose. |
DB_PROVIDER | plain postgres | Set to supabase to use DATABASE_URL_SUPABASE instead. |
DATABASE_URL_SUPABASE | — | Only read when DB_PROVIDER=supabase. |
CAPTCHA | false | true puts Cloudflare Turnstile on register and login. |
CAPTCHA_CLIENT_KEY | — | Turnstile site key. Required when CAPTCHA is on. |
CAPTCHA_SECRET | — | Turnstile secret key. With CAPTCHA on but this unset, nothing is verified. |
MAX_WATCH_SERIES | 777 | How many distinct titles the watch counter keeps before dropping the least recently watched. |
RESET_METRICS | false | Set to true for exactly one boot to wipe persisted counters, then remove it. |
Metrics#
Counters survive restarts on their own — a snapshot is written to a metrics
table in the same Postgres database, so there is nothing extra to mount.
MAX_WATCH_SERIES exists because title ids never stop arriving. The set of
watched series is capped and the least recently watched fall off the end;
raise it for a longer tail at the cost of a larger /metrics response.
Keep Postgres off the internet#
Docker writes its own iptables rules and goes straight through ufw, so a
published 5432 is your database on the public internet regardless of what
your firewall says. Use expose rather than ports, and let the backend
reach it by service name.