This content is for the 1.0 version. Switch to the latest version for up-to-date documentation.
A single .env file at the repo root configures the entire stack: docker-compose.yml
interpolates ${VAR} into the redis, livekit, ingress, egress and caddy services,
and passes the whole file to the core container via env_file: .env. The template with
inline comments lives at .env.example.
install.sh writes .env for you with strong random secrets and sane values derived from
the domain you give it. You normally never hand-edit it on a fresh install:
The single public domain Caddy/nginx fronts (dashboard + API + LiveKit /rtc). Needs a DNS-only A record — do not proxy it through a CDN, WebRTC media is UDP and won’t traverse an HTTP proxy. Use localhost for a local self-signed test. Read by Caddy/nginx/install.sh, not by the core process itself.
The ws/wss URL handed to browser clients in join links and tokens. Real domain: wss://<domain> (rides the single TLS vhost via /rtc). Localhost: ws://127.0.0.1:7880.
RTMP_PUBLIC_HOST
(empty)
Public host advertised in RTMP publish URLs handed to clients.
PUBLIC_BASE_URL
(empty)
Absolute base URL used to build HLS/player/embed/sample links (and the yolo plugin’s public base). Empty = derive from the incoming request host. Takes precedence over STREAMHUB_PUBLIC_URL wherever both are checked.
STREAMHUB_PUBLIC_URL
(empty)
Fallback base URL (samples, the cluster join-command hint) used when PUBLIC_BASE_URL is unset. install.sh sets this to https://<domain>.
STREAMHUB_APP_URL
https://app.streamhub.studio
Public base URL of the dashboard SPA, used to build magic-link, invite and password-reset URLs in outgoing email.
Redis used by LiveKit ingress/egress coordination and BullMQ job queues.
REDIS_BIND
127.0.0.1
Space-separated list of IPs the redis container binds to. Add a private IP here to serve edge nodes (paired with a password).
REDIS_PASSWORD
(empty = auth disabled)
Password for the local Redis container (--requirepass).
STREAMHUB_CLUSTER_REDIS_URL
(unset)
Redis URL handed back to a joining edge node in the /cluster/join response, so it attaches to the same LiveKit coordination Redis. Unset → returned as null (the edge keeps its local default).
Signs the dashboard login JWT (POST /api/v1/auth/login).
ADMIN_USER
(empty)
Break-glass dashboard admin username. Login is disabled if either ADMIN_USER or ADMIN_PASS is empty.
ADMIN_PASS
(empty)
Break-glass dashboard admin password.
STREAMHUB_API_TOKEN
sk_change_me
Global API bearer token, seeded into the DB by deploy/seed-token.js (the DB keeps only a hash). Kept in .env only so re-running the installer stays idempotent.
STREAMHUB_AUTHZ_ENFORCE
on
RBAC + quota + per-app-token isolation mode: off
STREAMHUB_ALLOW_SIGNUP
(unset = off)
Public self-signup (POST /auth/signup + “Create account” in the dashboard). 1/true/on/yes = anyone can sign up and get their own free-plan tenant. Unset = invite-only: a brand-new email gets 403; an invited pending user can still complete signup.
STREAMHUB_SUPERADMIN_EMAIL
info@streamhub.studio
Email that becomes the superadmin principal the moment it signs in via magic-link.
AUTH_RATE_LIMIT_MAX
10
Max attempts per client IP per window on the sensitive auth routes only (login, magic-link, magic/verify, reset-request, reset). The rest of the API, including dashboard polling, is not limited.
Magic-link login, invites and password resets. Without STREAMHUB_SMTP_HOST and
STREAMHUB_SMTP_PASS set, sends are skipped — logged, never crash the request.
Variable
Default
Description
STREAMHUB_SMTP_HOST
mail.wipermax.online
SMTP server. The built-in default is the vendor’s own mail host — set your own in production.
Used when a box joins an existing control plane via POST /api/v1/cluster/join
(install.sh --join). On a single-node install, leave this unset.
Variable
Default
Description
STREAMHUB_CLUSTER_TOKEN
(unset = joining disabled)
Shared secret an edge node must send as X-Cluster-Token to POST /cluster/join and /cluster/heartbeat. The /cluster/join endpoint returns 503 while this is unset. Must be long and random in production.
See also STREAMHUB_CLUSTER_REDIS_URL (Redis section above) and LIVEKIT_REDIS_ADDRESS /
LIVEKIT_REDIS_PASSWORD / LIVEKIT_WEBHOOK_URL (LiveKit section above), which are the
values an edge node actually receives from a successful join.
Where core (and egress) read/write data/streamhub.db, apps/<name>/{recordings,hls,snapshots,samples}, logs/, sdk/. Must be identical for core and egress — egress writes MP4s exactly where core looks to upload them to S3.
STREAMHUB_HOST_DATA_DIR
./data
Host path bind-mounted to DATA_DIR in both the core and egress containers (Compose only).
SDK_DIR
<DATA_DIR>/sdk
Where core serves the /sdk/* browser SDK from.
STREAMHUB_SNAPSHOT_SOURCE
(auto)
Override for the on-demand snapshot capture source.
Retention window for operational logs: purges server_logs DB rows and rotated log files older than this. 0 disables age-based purging (the file count cap still applies). The purge runs ~1 minute after boot and every 6 hours.
These are not read by core — they are Compose mem_limit/cpus keys applied directly to
the egress and ingress containers, the hard caps that stop a runaway session from taking
down the host.
Variable
Default
Description
EGRESS_MEM_LIMIT
2g
RAM cap for the egress container. Egress drives a headless Chrome to composite rooms — it is the single biggest OOM risk on a small host.
EGRESS_CPUS
2
CPU cap for egress.
INGRESS_MEM_LIMIT
1g
RAM cap for the ingress container.
INGRESS_CPUS
1.5
CPU cap for ingress.
See Limitations & capacity for measured RAM cost per recording
and why the default EGRESS_MEM_LIMIT matters.