Live Streaming
RTMP ingest, OBS setup, multistream, recording to VOD
NixStream includes a dedicated live streaming server (bin/media-live) for RTMP ingest and HLS/DASH output. Create streams in the admin panel, push from OBS, and viewers watch through the player or share links.
Live streaming is fine for personal streams and occasional events. Running many concurrent live channels on one small server is not recommended. CPU, packaging, and ops overhead add up quickly.
How it works
RTMP ingest listens on port 1935 (OBS or ffmpeg push). The media API is on 8888 for stream control and health checks.
In Docker, the live server runs as the nixstream_live container. On manual installs, you start bin/media-live directly or via Supervisor.
Create a stream
- Open Live in the admin panel
- Click Create stream and give it a title
- Optionally enable Save recording as VOD (off by default)
- Copy the stream key and RTMP URL
- The RTMP URL format is:
rtmp://{MEDIA_SERVER_INGEST_IP}:1935/live/{stream_key}
MEDIA_SERVER_INGEST_IP is auto-detected by install.sh in Docker. On manual installs, set it to your server's public IP.
OBS setup
- Open OBS and go to Settings > Stream
- Set service to Custom
- Server:
rtmp://your-server-ip:1935/live - Stream key: paste the key from the admin panel
- Click Start Streaming
Make sure port 1935 is open in your firewall and any cloud security group. RTMP uses TCP.
Recommended OBS settings
- Output: x264 or hardware encoder (NVENC if available)
- Keyframe interval: 2 seconds (matches typical HLS segment length)
- Bitrate: 2500-6000 kbps for 1080p depending on your audience bandwidth
- Use H.264 + AAC when you plan to restream (destinations must accept the source codecs)
Multistream (restream)
Restream forwards the live packets to other platforms. It does not transcode per destination.
- Open Settings > Live streaming (Multistreaming)
- Enable a platform and set its RTMP ingest URL and stream key (YouTube, Twitch, etc.)
- Start your live stream from OBS
- Open the stream in the panel and toggle destinations under Multistreaming (only available while the stream is active)
Notes:
- Destinations must accept the codec OBS is sending (H.264 + AAC is safest)
- Viewer ABR (HLS ladder) is separate from restream; enabling transcoding affects panel playback, not the RTMP push
- Destinations are not auto-started when you go live. Toggle them while active.
- Instagram/TikTok often need special ingest; plain RTMP may fail even when NixStream pushes correctly
Recording > VOD
Recording is optional. Enable Save recording as VOD on the stream when you create or edit it. When enabled, media-live writes an FLV and archives it under storage/app/public/live_archive/ when the stream ends.
Import archived recordings into the normal VOD encoding queue:
docker compose exec app php artisan live:import-recordingsThe scheduler runs this every five minutes. Imports use the tenant default VOD encoding profile and default storage provider (same as manual uploads). Finished recordings show up in Media > VOD > Live Recordings under your library root.
To link recordings that were encoded before this folder existed:
docker compose exec app php artisan live:import-recordings --backfill-orphansAfter VOD encoding succeeds, the FLV and live session directories are removed so disk usage stays bounded.
Live playback security
Live HLS segments on disk are not AES-128 encrypted. That's expected. Live security is enforced by signed Edge URLs:
- Panel and embed players only receive Edge URLs (
EDGE_BASE_URL), not direct paths tostorage/app/media/live/. - Each playback session uses an opaque URL prefix (
p/…); stream keys never appear in manifests or segment URLs. - Sessions expire after
EDGE_SESSION_TTL_LIVE(default 6 hours). Expired URLs return 403.
VOD uses segment AES-128 plus signed URLs. Live uses signed URLs only. See Encoding, Edge delivery.
Optional Require user or share-link auth (Settings > Integrations > Edge) binds live sessions to a user or share link for instant revocation. See Security, Playback authorization.
Stale live_media sessions and old archives are purged hourly:
docker compose exec app php artisan live:cleanup
docker compose exec app php artisan live:cleanup --dry-runLocal E2E (not in the repository)
Operator live/encoding smoke tests (live:test, encoding:test-profiles) are local-only scripts and are gitignored. They are not required to run NixStream.
Firewall rules
Open these ports on your server:
sudo ufw allow 1935/tcp # RTMP ingest
sudo ufw allow 8888/tcp # Media APIHealth check
Confirm the live server is responding:
curl http://localhost:8888/healthCheck logs:
docker compose logs nixstream-liveClient API
Monitor streams programmatically:
GET /api/client/v1/live-streamslists all streams (paginated)GET /api/client/v1/live-streams/activereturns currently broadcasting streamsGET /api/client/v1/live-streams/{id}returns a single stream with manifest URLs
Active streams include a manifest object with HLS playback URLs.
Environment variables
| Variable | Purpose |
|---|---|
MEDIA_SERVER_HOST | Internal API URL (e.g. http://nixstream-live:8888) |
MEDIA_SERVER_API_KEY | Auth key for media server API |
MEDIA_SERVER_INGEST_IP | Public IP or hostname for RTMP |
MEDIA_JWT_SECRET | JWT for stream tokens |
MEDIA_LIVE_API_PORT | Default 8888 |
MEDIA_LIVE_RTMP_PORT | Default 1935 |
Troubleshooting live streams
OBS can't connect
- Verify port 1935 is open externally
- Confirm
MEDIA_SERVER_INGEST_IPis your public IP, not127.0.0.1 - Check that
nixstream_livecontainer is running
Stream shows inactive in admin panel
- The media server may be down. Run
curl http://localhost:8888/health - Verify
MEDIA_SERVER_API_KEYmatches between app and live server
Playback stutters
- Lower your OBS output bitrate
- Check server CPU during the stream
- Confirm adequate bandwidth on the ingest path
Restream fails
- Confirm the stream is active before toggling destinations
- Check platform RTMP URL and stream key in Settings > Live streaming
- Use H.264 + AAC from OBS
See the full troubleshooting guide for more scenarios.