Cloneable example that bundles MistServer, Prometheus, and Grafana with ready-to-use VOD assets and a live ingest helper. The default setup compiles MistServer from source with -DWITH_AV=true for hardware-accelerated transcoding support, provisions Prometheus scraping, and loads a starter Grafana dashboard.
- Custom MistServer image built from the official
developmentbranch with AV acceleration enabled. - Pre-baked configuration at
configs/mistserver.confmounted read/write for easy updates through the Mist UI. - Sample VOD assets & simulated livestream preconfigured in Mist.
- Prometheus configured to scrape MistServer metrics and Grafana auto-provisioned with a baseline dashboard and Prometheus datasource.
- Default
--shm-size=1gbto avoid the 64 MB Docker shared-memory limit.
docker compose up --buildLinux environment is recommended, but Docker Desktop works too. This starts only the core services (Mist, Prometheus, Grafana, etc.) and publishes their ports directly. Caddy is disabled by default so ports 80/443 stay free. You can enable it using:
docker compose --profile caddy up --build, or setCOMPOSE_PROFILES=caddyin.envif you always want the proxy running.
| Service | URL / Endpoint | Notes |
|---|---|---|
| MistController UI | http://localhost:4242 | Defaults to admin / admin unless overridden via env. |
| MistPlayer templates | http://localhost:8080/{stream}.html | Replace {stream} with vod, live, push, etc. |
| RTMP ingest | rtmp://localhost:1935/live/{stream} | Push FLV/RTMP inputs (obs → live/{stream}). |
| Prometheus | http://localhost:9090 | Scrapes Mist metrics every 10 s. |
| Grafana | http://localhost:3000 | Default login admin / admin; auto-provisioned dashboard. |
Want friendlier URLs without buying a domain? Start the Caddy profile on plain HTTP:
docker compose --profile caddy up --buildThis keeps the published ports above, but also serves:
http://localhost/mist/(plushttp://localhost/mist/ws)http://localhost/view/,http://localhost/hls/,http://localhost/webrtc/http://localhost/grafana/
Set COMPOSE_PROFILES=caddy in .env if you always want this proxy when running docker compose up.
- Copy
env.exampleto project root as.envand set at leastDOMAIN(e.g.stream.example.com). - Point DNS for
DOMAINto this host and open ports 80/443. - Start with the Caddy profile enabled:
(Or set
docker compose --profile caddy up --build
COMPOSE_PROFILES=caddyin.envso a plaindocker compose upalso starts Caddy.)
When DOMAIN is set, Caddy will terminate TLS and proxy:
| Public path | Backend | Notes |
|---|---|---|
https://$DOMAIN/mist/ |
MistController UI (port 4242) | Admin/API surface. |
wss://$DOMAIN/mist/ws |
MistController WebSocket | Live stats/socket UI channel. |
https://$DOMAIN/view/ |
Mist viewer endpoints (port 8080) | Handles WebSocket upgrades for DRM/dev panels. |
https://$DOMAIN/hls/ |
Mist HLS | Served with permissive CORS headers. |
https://$DOMAIN/webrtc/ |
Mist WebRTC | Signalling + media for browsers. |
https://$DOMAIN/grafana/ |
Grafana (port 3000) | Served from /grafana/ sub-path. |
If DOMAIN is unset but you run the caddy profile, it still exposes the same paths on plain HTTP (http://localhost/...) alongside the direct service ports above.
docker compose -f docker-compose.yml -f docker-compose.gpu.yml up --builddocker-compose.gpu.yml grants the container access to NVIDIA GPUs (gpus: all) when the NVIDIA Container Toolkit is installed, and exposes /dev/dri for Intel Quick Sync / VA-API. If your distribution restricts /dev/dri to the render group, edit the commented group_add line with your render group GID (getent group render). Docker Desktop on macOS/Windows does not expose host GPUs to Linux containers, so this override has no effect there.
Need both GPU access and the reverse proxy? Just add the profile flag:
docker compose --profile caddy -f docker-compose.yml -f docker-compose.gpu.yml up --buildPrefer a single command that sanity-checks your .env, domain, and port availability before starting? Use the bundled Makefile.
make up runs scripts/preflight.sh by default (validates DOMAIN syntax/DNS, port 80/443 availability when Caddy is enabled, and numeric env vars). The Makefile simply wraps the raw docker compose commands shown here.
| Command | Purpose |
|---|---|
make up |
Preflight + docker compose up --build |
make down |
Stop the stack (docker compose down) |
make logs |
Tail service logs (docker compose logs -f) |
make ps |
Show service status |
make build |
Preflight + docker compose build |
make preflight |
Run the checks only |
Supported flags (pass them alongside the command, e.g. make up CADDY=true GPU=true DETACH=true):
| Flag | Default | Description |
|---|---|---|
CADDY=true |
false | Adds the reverse-proxy profile (ports 80/443). |
GPU=true |
false | Includes docker-compose.gpu.yml overrides (NVIDIA/VA-API). |
DETACH=true |
false | Runs docker compose up -d. |
PREFLIGHT=false |
true | Skip scripts/preflight.sh (not recommended unless already run). |
What preflight checks do:
- Sanitizes
DOMAIN, verifies it resolves via the host resolver, and ensures at least one resolved IP matches a local interface (helps catch DNS pointing elsewhere). - When
CADDY=true, confirms ports 80/443 are free and—if binding is possible—spawns a one‑shot HTTP listener on those ports, thencurlshttp://DOMAIN/.well-known/...to mimic ACME reachability. A failure usually means the host can’t bind the port, DNS isn’t updated yet, or port forwarding/firewall blocks traffic. - Validates numeric/bool env vars before
configs/mistserver.confis rewritten.
Optional escape hatches (set as env vars only if you truly need to disable parts of the preflight):
| Env var | Default | Effect |
|---|---|---|
SKIP_DOMAIN_DNS_CHECK=true |
false | Don’t resolve DOMAIN. |
SKIP_DOMAIN_IP_MATCH=true |
false | Skip comparing DNS answers against local interfaces. |
SKIP_PORT_CHECK=true |
false | Skip the port-80/443 availability probe. |
SKIP_PORT_SELFTEST=true |
false | Skip the ACME-style listener/curl loopback. |
- Default streams:
vod: on-demand playback from assets inassets/vod/.live: simulated livestream using ffmpegpush: stream ready to accept RTMP inputs
- Metrics are exposed at
http://localhost:4242/metrics. Prometheus scrapes this endpoint every 10 seconds.
- To emit a synthetic FFmpeg test signal to the stream named 'push':
./scripts/videogen.sh -f flv -ac 2 rtmp://localhost:1935/live/push
- To playback the result (substitute your favourite player):
ffplay http://localhost:8080/hls/push/index.m3u8 ffplay http://localhost:8080/cmaf/push/index.m3u8 ffplay http://localhost:8080/webrtc/push
- On the MistController UI (http://localhost:4242) you can view the stream and some basic QoE metrics
- The embedded MistPlayer allows you to seamlessly switch between protocols, like WebRTC: http://localhost:8080/push.html?dev=1
On container start, a small script rewrites configs/mistserver.conf from environment:
ADMIN_USER/ADMIN_PASSWORD(password stored as MD5)BANDWIDTH_EXCLUDE_LOCALand optionalBANDWIDTH_LIMIT_MBITLOCATION_NAME,LOCATION_LAT,LOCATION_LONPROMETHEUS_PATH(defaults tometrics)- If
DOMAINis set, HTTP pub addresses and WebRTCpubhostare updated accordingly
Grafana loads the MistServer Overview dashboard automatically. Start with the example panels (target status, scrape duration, latest metrics) and extend the queries as you explore the Mist metrics namespace. To import community dashboards, drop the JSON in grafana/dashboards and restart Grafana.
docker compose downAdd --volumes to drop Prometheus/Grafana data directories.