How StreamHub is built: one domain, one image
A single reverse proxy, one core container that serves the API, dashboard, HLS and SDK, and a hidden SFU underneath. A tour of the request routing and the pieces that make it feel like one product.
Everything behind one domain
The whole product lives at one hostname. A reverse proxy (Caddy or nginx) terminates TLS and splits traffic two ways:
/rtc→ LiveKit signaling (WebRTC).- everything else (
/,/api/v1,/hls,/sdk,/samples,/metrics) → the core service on127.0.0.1:3020.
WebRTC media (UDP), RTMP (1935) and WHIP (8080) reach the server directly. From the outside it looks like a single application, not a cluster of services.
One core image serves everything
The core is a NestJS app, and its container is deliberately all-in-one. A multi-stage build compiles three things into it:
- the React dashboard (built and served as static files),
- the browser SDK (an IIFE bundle dropped into
/sdk), - the NestJS core itself (REST API + HLS + webhook sink + job runner).
So shipping a dashboard change means rebuilding the core image — there is no separate frontend deployment to coordinate.
The SFU is an implementation detail
LiveKit, its ingress and egress workers, and Redis run as sibling containers, but they are hidden behind the core. The operator never runs docker compose by hand:
streamhub status # every service's health
streamhub logs core -f
streamhub upgrade # idempotent, backed-up, rollback-able
The streamhub CLI knows which services a node runs (from its install profile) and fronts them as a single systemd unit, so systemctl status streamhub reflects the entire app.
Data lives close to each app
State is split for isolation. A small global SQLite database holds tenants, users, API tokens and the app registry. Every app then owns its own app.db with its streams, VODs and ingress auth. The data directory — recordings, HLS segments, snapshots — is bind-mounted into both the core and egress, so egress writes MP4s exactly where the core expects to find and upload them.
Why the shape matters
One domain, one image, one CLI is not just tidy — it is what makes a self-hosted media server operable by a small team. There is one thing to deploy, one thing to monitor, one thing to upgrade, and a clean seam (the reverse proxy) between the SFU and the management layer on top of it.