← Back to the blog
Concepts #architecture

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.

2 min read
Leer en español

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 on 127.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.