Developing HydraSRT
July 17, 2026 · View on GitHub
Local development, release builds, and Docker usage.
Prerequisites
System Dependencies
Install:
-
Elixir (version 1.18.4 or later)
-
Erlang/OTP (version 27.3 or later)
-
Node.js and npm (version 24.2.0 or later, for building the web UI)
Use asdf or another version manager.
.tool-versionscurrently pins:- Elixir 1.18.4-otp-27
- Erlang 27.3.4.1
- Node.js 24.2.0
-
Rust, Cargo, GStreamer, and related libraries for the streaming pipeline:
# Ubuntu/Debian sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \ libsrt-openssl-dev libglib2.0-dev pkg-config cargo rustc # macOS (using Homebrew) brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad \ srt pkg-config rust -
Verify streaming pipeline dependencies are correctly installed:
pkg-config --libs gstreamer-1.0 gstreamer-base-1.0 glib-2.0 srtThis command should print linker flags. If it errors, install the missing GStreamer/SRT packages.
Local Development
make dev starts Phoenix and the Vite dev server.
First-time Setup
mix setup
Starting the Development Server
make dev
The web UI dev server is fixed to:
host: localhostport: 5173strictPort: true
If 5173 is busy, Vite fails instead of selecting another port.
Override for Docker/remote dev:
VITE_DEV_HOST(example:0.0.0.0)VITE_DEV_PORT(example:5173)VITE_DEV_STRICT_PORT(example:false)
cd web_app
VITE_DEV_HOST=0.0.0.0 VITE_DEV_STRICT_PORT=false yarn dev
Demo Mode
DEMO_DATA=true creates disabled demo routes.
DEMO_DATA=true make dev
When demo mode is enabled:
ffmpegmust be available inPATH(startup fails if missing)- five routes are created automatically (idempotent):
demo_route(SRT source):- source:
srt://127.0.0.1:4200?mode=caller - destinations:
srt://127.0.0.1:4211?mode=listenerudp://127.0.0.1:4212(for local playback)
- source:
demo_udp_route(UDP source):- source:
udp://127.0.0.1:4201 - destinations:
srt://127.0.0.1:4213?mode=listenerudp://127.0.0.1:4214(for local playback)
- source:
demo_rtp_route(RTP source):- source:
rtp://127.0.0.1:4202 - destinations:
srt://127.0.0.1:4205?mode=listenerudp://127.0.0.1:4206(for local playback)
- source:
demo_rtmp_route(RTMP source):- source:
/live/test - destinations:
srt://127.0.0.1:4215?mode=listenerrtmp://127.0.0.1:1935/demo/routetest
- source:
demo_rtmp-client_route(SRT source → UDP + RTMP client):- source:
srt://127.0.0.1:4200?mode=caller - destinations:
udp://127.0.0.1:4216(for local playback; port 4216 avoids bind conflict withdemo_routeUDP on 4212)rtmp://127.0.0.1:1935/live/stream
- source:
- routes are created with
enabled: false(not auto-started)
After startup:
- Open http://localhost:5173/#/settings/signal-generation/srt (or
/udp,/rtp,/rtmp) - Click
Startin theSignal generationsection for the active tab - Start the matching demo route from the Routes UI:
- SRT tab →
demo_route - UDP tab →
demo_udp_route - RTP tab →
demo_rtp_route - RTMP tab →
demo_rtmp_route - SRT → RTMP client →
demo_rtmp-client_route(start SRT signal generation, then this route)
- SRT tab →
Verify playback:
# demo_route SRT destination
ffplay -fflags nobuffer -flags low_delay -i "srt://127.0.0.1:4211?mode=caller"
# demo_route UDP destination
ffplay -fflags nobuffer -flags low_delay -i "udp://@:4212"
# demo_udp_route UDP destination
ffplay -fflags nobuffer -flags low_delay -i "udp://@:4214"
# demo_rtp_route UDP destination
ffplay -fflags nobuffer -flags low_delay -i "udp://@:4206"
# demo_rtmp_route SRT destination
ffplay -fflags nobuffer -flags low_delay -i "srt://127.0.0.1:4215?mode=caller"
# demo_rtmp_route RTMP destination
ffplay -fflags nobuffer -flags low_delay -i "rtmp://127.0.0.1:1935/demo/routetest"
# demo_rtmp-client_route UDP destination
ffplay -fflags nobuffer -flags low_delay -i "udp://@:4216"
# demo_rtmp-client_route RTMP destination (Hydra RTMP proxy on 127.0.0.1:1935)
ffplay -fflags nobuffer -flags low_delay -i "rtmp://127.0.0.1:1935/live/stream"
Environment Variables
See envs.md.
Building for Production
HydraSRT is beta. Validate upgrades before production rollout.
-
Clone the repository:
git clone https://github.com/streamband/hydra-srt.git cd hydra-srt -
Build the release:
mix deps.get cd web_app && npm install && cd .. MIX_ENV=prod mix compile MIX_ENV=prod mix releaseThe release compiles Elixir, builds the Rust pipeline, builds the web app, and packages the release.
Running in Production
Use start_iex when you want an interactive shell.
-
Interactive shell:
PHX_SERVER=true DATABASE_PATH=/etc/hydra_srt/hydra_srt.db API_AUTH_USERNAME=your_username API_AUTH_PASSWORD=your_password _build/prod/rel/hydra_srt/bin/hydra_srt start_iexDaemon mode:
PHX_SERVER=true DATABASE_PATH=/etc/hydra_srt/hydra_srt.db API_AUTH_USERNAME=your_username API_AUTH_PASSWORD=your_password _build/prod/rel/hydra_srt/bin/hydra_srt start -
Release commands:
_build/prod/rel/hydra_srt/bin/hydra_srt stop _build/prod/rel/hydra_srt/bin/hydra_srt remote _build/prod/rel/hydra_srt/bin/hydra_srt -
Open the UI:
http://your_server_ip:40004000is the default port. Override withPORT.
Running with Docker
The repository includes Compose files for the recommended Docker workflow.
Quick Start with Docker Compose
Build the local image:
docker compose build
Start on native Linux:
docker compose up
On first start (fresh ./data/db volume), the container will automatically run DB migrations.
To disable auto-migrations, set RUN_MIGRATIONS=false.
docker-compose.yml uses DATABASE_PATH=/app/db/hydra_srt.db, stores route metadata under ./data/db, and starts VictoriaMetrics plus VictoriaLogs with 3-day retention for historical metrics, events, and pipeline logs.
The default Compose file uses network_mode: "host" so HydraSRT can bind directly to the host network. This is intended for native Linux, including WSL2 only when Docker Engine runs inside the WSL distro.
If your Docker Engine cannot use Linux host networking, start with the ports override:
docker compose -f docker-compose.yml -f docker-compose.ports.yml up
To override the DB path, credentials, demo mode, or POOL_SIZE, create a .env file:
echo "API_AUTH_USERNAME=admin" > .env
echo "API_AUTH_PASSWORD=password123" >> .env
echo "DATABASE_PATH=/app/db/hydra_srt.db" >> .env
echo "DEMO_DATA=false" >> .env
echo "POOL_SIZE=1" >> .env
Open the UI:
http://127.0.0.1:4000
Log in with API_AUTH_USERNAME and API_AUTH_PASSWORD.
Stop:
docker compose down
Docker Networking
Docker networking modes:
- Default (recommended on native Linux): host networking through
network_mode: "host". - Ports override: bridge networking with explicit port mappings through
docker-compose.ports.yml.
Default mode: host network
docker compose up --build
Web UI:
http://127.0.0.1:4000
Host network means:
- The container uses the host IP and interfaces.
- Container ports are exposed on the host network.
- Port conflicts must be handled on the host.
Ports override
docker compose -f docker-compose.yml -f docker-compose.ports.yml up -d
To stop:
docker compose -f docker-compose.yml -f docker-compose.ports.yml down
Use the ports override when Linux host networking is not available or not desired. Docker Desktop-backed setups do not provide the same host network behavior as a native Linux Docker Engine. On WSL2, host networking is only the expected Linux behavior when Docker Engine runs inside the WSL distro.
Running the Published Image Directly
Use direct docker run only when you do not want to use the repository Compose files.
Prebuilt Docker image:
Native Linux:
docker run --rm --network host \
-v "$(pwd)/data/db:/app/db" \
-e PHX_SERVER=true \
-e DATABASE_PATH=/app/db/hydra_srt.db \
-e VICTORIA_METRICS_URL=http://127.0.0.1:8428 \
-e VICTORIA_LOGS_URL=http://127.0.0.1:9428 \
-e API_AUTH_USERNAME=admin \
-e API_AUTH_PASSWORD=password123 \
streamband/hydra-srt:latest
Fallback with explicit port mappings:
docker run --rm -p 4000:4000 \
-p 1935:1935 \
-p 4100-4500:4100-4500/udp \
-v "$(pwd)/data/db:/app/db" \
-e PHX_SERVER=true \
-e DATABASE_PATH=/app/db/hydra_srt.db \
-e VICTORIA_METRICS_URL=http://host.docker.internal:8428 \
-e VICTORIA_LOGS_URL=http://host.docker.internal:9428 \
-e API_AUTH_USERNAME=admin \
-e API_AUTH_PASSWORD=password123 \
streamband/hydra-srt:latest
Troubleshooting
-
Streaming pipeline:
- Check dependencies:
pkg-config --libs gstreamer-1.0 gstreamer-base-1.0 glib-2.0 srt - Rebuild the Rust binary with
mix compile.rs_native
- Check dependencies:
-
Web app:
- Check Node/npm versions
- Build manually:
cd web_app && npm install && npm run build
-
Elixir app:
- Check required environment variables
Testing and Quality
mix q
mix test
Other useful commands:
E2E=true mix test --only e2e
cd native && cargo test
cd web_app && npm run test:unit
cd web_app && npm run test:e2e
make test_ci_local