Skip to content

Deploy

This content is for the 1.0 version. Switch to the latest version for up-to-date documentation.

StreamHub supports two deploy shapes on top of the same single-node architecture. Both need a Linux host with a public IP — LiveKit uses host networking for UDP media and STUN external-IP detection, so Docker Desktop on macOS is not a target.

  • A. Docker Compose + Caddy — the OSS quick-install path. One command, automatic TLS.
  • B. systemd + nginx + certbot — the plain-server path (used in production on stream01).

The deployable unit is the NestJS core (which also serves the compiled React web dashboard as static assets), the LiveKit stack (server + ingress + egress + redis), and the browser SDK:

Terminal window
# streamhub-core (the brain + SPA host)
cd streamhub-core
npm ci
npm run build # → dist/ (nest build); runtime entry: node dist/main.js
# streamhub-web (React SPA) — its dist/ is served by core as static assets
cd ../streamhub-web
npm ci && npm run build # → dist/ (Vite + Tailwind)
# streamhub-adaptor (browser SDK) — the drop-in AntMedia WebRTCAdaptor shim
cd ../streamhub-adaptor
npm ci && npm run build # → dist/streamhub-adaptor.global.js

With Docker Compose these builds happen inside the image (deploy/Dockerfile) — you never run them by hand.

Terminal window
curl -fsSL https://www.streamhub.studio/install.sh | sudo bash

install.sh is idempotent and:

  1. checks prerequisites (docker + the compose plugin, openssl, curl);
  2. clones the repo if run standalone (into ./vision-media-server);
  3. prompts for STREAMHUB_DOMAIN, ADMIN_PASS, ACME_EMAIL — or reuses an existing .env. Pre-set any of STREAMHUB_DOMAIN / ADMIN_USER / ADMIN_PASS / ACME_EMAIL in the environment to run it non-interactively;
  4. generates a .env with strong random secrets: LIVEKIT_API_KEY / LIVEKIT_API_SECRET, STREAMHUB_JWT_SECRET, ADMIN_PASS, STREAMHUB_API_TOKEN (sk_…);
  5. builds and starts the stack: docker compose up -d --build;
  6. waits for core health, then seeds the global API token into the DB (deploy/seed-token.js).
Terminal window
cp .env.example .env # then edit — see the ENV reference
docker compose up -d --build
docker compose ps # redis, livekit, ingress, egress, core, caddy → healthy

Compose services (docker-compose.yml): redis (7-alpine), livekit (livekit/livekit-server:v1.8.4), ingress, egress, core (streamhub-core:local, built from deploy/Dockerfile), and caddy (2-alpine, deploy/Caddyfile, automatic TLS). Caddy routes /rtc to LiveKit and everything else to core, on one TLS vhost.

Re-running the installer is the supported update path — it’s idempotent and reuses the existing .env:

Terminal window
curl -fsSL https://www.streamhub.studio/install.sh | sudo bash

Or update manually from a checked-out repo:

Terminal window
git pull
docker compose up -d --build
docker compose logs -f core # watch it come back up

.env changes are only fully picked up on down + up -d (a recreate), not on restart.

DB migrations — automatic, idempotent, backed up

Section titled “DB migrations — automatic, idempotent, backed up”

Migrations run at core boot (DbService.init) — you never run them by hand. They:

  • create data/streamhub.db (global) and seed the default live app on a fresh install;
  • apply GLOBAL_MIGRATIONS, idempotent GLOBAL_COLUMN_ADDS and GLOBAL_TENANCY_BACKFILL;
  • open each apps/<app>/app.db lazily and apply APP_MIGRATIONS, importing any legacy apps/<app>/vods.db (left in place as a backup);
  • perform the per-app split (streams/vods/ingress_auth global → app.db) once, guarded by a marker in _streamhub_meta, after taking a VACUUM INTO backup of the global DB as streamhub.db.bak-<timestamp>.

Everything is CREATE TABLE IF NOT EXISTS plus copy-if-absent, so re-running is safe (redeploys, restarts, re-running the installer). Rolling forward is just: deploy the new build and restart core — migrations self-apply.

Core serves the SDK statically from SDK_DIR (default <DATA_DIR>/sdk) at /sdk/streamhub-adaptor.global.js. On a manual deploy, place the built adaptor there:

Terminal window
cp streamhub-adaptor/dist/streamhub-adaptor.global.js "$DATA_DIR/sdk/"

The Docker image does this during build. A missing file simply 404s — sample pages then fall back to plain livekit-client, so this step is non-fatal but recommended.

Terminal window
curl -s https://streamhub.example.com/api/v1/health # {"status":"ok",...}
# /metrics is denied at the vhost (403); scrape it locally on the host:
curl -s http://127.0.0.1:3020/metrics | grep streamhub_
# authed:
curl -s -H "Authorization: Bearer $STREAMHUB_API_TOKEN" \
https://streamhub.example.com/api/v1/stats
# Swagger UI:
# https://streamhub.example.com/api/v1/docs

Then exercise a real path: create an app, push RTMP (ffmpeg … -f flv rtmp://HOST:1935/live/<key>), see it under Streams, start a recording, and confirm the VOD lands in the app’s S3 bucket as ready with a working presigned URL.