This guide covers deploying Slyber TURN Server in a Docker container, configuring authentication modes, and verifying all system nodes during testing.
Choosing an Authentication Mode
Before starting the container, select the WebRTC client credential verification mode using the AUTH_MODE environment variable:
AUTH_MODE="secret"(standalone secret key mode): the server operates without external databases and validates temporary passwords using the HMAC-SHA1 algorithm based on the sharedTURN_SECRETkey. Suitable for rapid deployment when your backend only needs to issue temporary tokens to clients. Check out the HTTP API.AUTH_MODE="redis"(dynamic mode via Redis): the server checks for the user key in the Redis database on every connection. This allows you to view active sessions in real time, manage their TTL, and instantly disconnect a specific participant right during a call by deleting their key from the database.
By default, with PUBLIC_IP="auto", the server automatically detects its external IPv4 address on startup, so manual IP configuration is not required.
Option 1. Running in secret Mode
Start the Slyber TURN Server container:
docker pull ghcr.io/slyber-app/slyber-turn-server:latest
docker rm -f slyber_turn 2>/dev/null || true
GEN_SECRET=$(head -c 16 /dev/urandom | od -An -tx1 | tr -d ' \n') && \
docker run -d \
--name slyber_turn \
--restart unless-stopped \
-p 3478:3478/udp \
-p 3478:3478/tcp \
-p 8080:8080/tcp \
-p 50000-50100:50000-50100/udp \
-e PUBLIC_IP="auto" \
-e REALM="turn.local" \
-e AUTH_MODE="secret" \
-e TURN_SECRET="${GEN_SECRET}" \
-e RELAY_MIN_PORT=50000 \
-e RELAY_MAX_PORT=50100 \
ghcr.io/slyber-app/slyber-turn-server:latest && \
sleep 1 && \
docker exec slyber_turn slyber_turn doctor && \
HOST_IP=$([ -n "${SSH_CONNECTION:-}" ] && curl -fsSL4 --max-time 2 ifconfig.me 2>/dev/null || echo 127.0.0.1) && \
echo " TURN / STUN URI : turn:${HOST_IP}:3478 (UDP/TCP)" && \
echo " HTTP API URL : http://${HOST_IP}:8080" && \
echo " Realm : turn.local" && \
echo " Auth Mode : secret (TURN_SECRET=${GEN_SECRET})" && \
echo " Test Creds API : curl -s \"http://${HOST_IP}:8080/api/turn-credentials?user_id=dev\" && echo" && \
echo " Run Benchmark : docker exec slyber_turn slyber_turn bench -clients 20 -duration 5s" && \
echo " Live Logs : docker logs -f slyber_turn" && \
echo "--------------------------------------------------"
Check the server status using the built-in doctor utility:
docker exec slyber_turn slyber_turn doctor
Verify temporary credential generation via the HTTP API.
In secret mode, the built-in API on port 8080 generates a ready-to-use username and password pair with a limited lifetime for the specified user_id:
curl -s "http://127.0.0.1:8080/api/turn-credentials?user_id=some_user_id" && echo ""
Option 2. Running in redis Mode
Start the Slyber TURN Server container connected to Redis:
docker network create slyber_net 2>/dev/null || true
docker rm -f slyber_redis 2>/dev/null || true
docker run -d \
--name slyber_redis \
--restart unless-stopped \
--network slyber_net \
-p 6379:6379/tcp \
redis:alpine redis-server --protected-mode no
docker pull ghcr.io/slyber-app/slyber-turn-server:latest
docker rm -f slyber_turn 2>/dev/null || true
docker run -d \
--name slyber_turn \
--restart unless-stopped \
--network slyber_net \
-p 3478:3478/udp \
-p 3478:3478/tcp \
-p 8080:8080/tcp \
-p 50000-50100:50000-50100/udp \
-e PUBLIC_IP="auto" \
-e REALM="turn.local" \
-e AUTH_MODE="redis" \
-e REDIS_ADDR="slyber_redis:6379" \
-e RELAY_MIN_PORT=50000 \
-e RELAY_MAX_PORT=50100 \
ghcr.io/slyber-app/slyber-turn-server:latest && \
sleep 2 && \
docker exec slyber_turn slyber_turn --version && \
docker exec slyber_turn slyber_turn doctor && \
HOST_IP=$([ -n "${SSH_CONNECTION:-}" ] && curl -fsSL4 --max-time 2 ifconfig.me 2>/dev/null || echo 127.0.0.1) && \
echo " TURN / STUN URI : turn:${HOST_IP}:3478 (UDP/TCP)" && \
echo " Redis Endpoint : ${HOST_IP}:6379 (internal: slyber_redis:6379)" && \
echo " HTTP Health URL : http://${HOST_IP}:8080/healthz" && \
echo " Realm : turn.local" && \
echo " Auth Mode : redis" && \
echo " Inspect Keys : docker exec slyber_redis redis-cli KEYS \"turn/realm/*\"" && \
echo " Live Redis Mon : docker exec -it slyber_redis redis-cli MONITOR" && \
echo " Run Benchmark : docker exec slyber_turn slyber_turn bench -clients 20 -duration 5s" && \
echo " Live Logs : docker logs -f slyber_turn" && \
echo "--------------------------------------------------"
Verify the TURN and Redis connection using the self-diagnostic utility:
docker exec slyber_turn slyber_turn doctor
Working with Redis During Testing: Where Keys Are Stored and How to Inspect Them
In AUTH_MODE="redis" mode, the server looks up keys formatted as turn/realm/<REALM>/user/<USERNAME>/key. The key value is a 32-character MD5 hash of the string <USERNAME>:<REALM>:<PASSWORD> according to RFC 5389.
1. Creating a Test User Manually
To create a user session_777 with the password pass_777 in the turn.local realm with a key TTL of 3600 seconds (1 hour), run:
On Linux:
KEY_HEX=$(printf "session_777:turn.local:pass_777" | md5sum | awk '{print $1}')
docker exec slyber_redis redis-cli SET "turn/realm/turn.local/user/session_777/key" "$KEY_HEX" EX 3600
On macOS:
KEY_HEX=$(printf "session_777:turn.local:pass_777" | md5 | tr -d ' \n')
docker exec slyber_redis redis-cli SET "turn/realm/turn.local/user/session_777/key" "$KEY_HEX" EX 3600
2. Viewing All Currently Active Users in Redis
To view the list of all accounts currently stored in the database (including peer pairs like p3_u1_da37e6 generated by the test suite):
docker exec slyber_redis redis-cli KEYS "turn/realm/*"
Example output:
1) "turn/realm/turn.local/user/session_777/key"
2) "turn/realm/turn.local/user/p3_u1_da37e6/key"
3) "turn/realm/turn.local/user/p3_u2_da37e6/key"
3. Checking a Specific User Hash
To inspect the stored MD5 key for a specific user:
docker exec slyber_redis redis-cli GET "turn/realm/turn.local/user/session_777/key"
4. Checking the Remaining Key Lifetime (TTL)
To check how many seconds remain before the session is automatically removed from Redis:
docker exec slyber_redis redis-cli TTL "turn/realm/turn.local/user/session_777/key"
5. Monitoring TURN Server Requests to Redis in Real Time
To verify that the server actively queries keys from the database when establishing a call or triggering an Ice restart, start the live Redis command monitor:
docker exec -it slyber_redis redis-cli MONITOR
6. Forcibly Disconnecting a User (Key Revocation)
To revoke a user's access, delete their key:
docker exec slyber_redis redis-cli DEL "turn/realm/turn.local/user/session_777/key"
Running with Docker Compose
To launch the entire infrastructure with a single command, save the following docker-compose.yml:
services:
slyber_redis:
image: redis:alpine
container_name: slyber_redis
restart: unless-stopped
ports:
- "127.0.0.1:6379:6379/tcp"
networks:
- slyber_net
slyber_turn:
image: ghcr.io/slyber-app/slyber-turn-server:latest
container_name: slyber_turn
restart: unless-stopped
depends_on:
- slyber_redis
networks:
- slyber_net
ports:
- "3478:3478/udp"
- "3478:3478/tcp"
- "127.0.0.1:8080:8080/tcp"
- "50000-50100:50000-50100/udp"
environment:
PUBLIC_IP: "auto"
REALM: "turn.local"
AUTH_MODE: "redis"
REDIS_ADDR: "slyber_redis:6379"
RELAY_MIN_PORT: "50000"
RELAY_MAX_PORT: "50100"
networks:
slyber_net:
driver: bridge
Starting and stopping the stack:
docker compose up -d
docker compose down
Server Verification and Debugging Cheat Sheet
View TURN server logs in real time: Shows the PUBLIC_IP detected at startup, the active AUTH_MODE, and relay port allocation events:
docker logs -f slyber_turn
Comprehensive container self-diagnostics: Checks the availability of UDP/TCP sockets on port 3478, HTTP API on port 8080, the relay port pool, and the Redis connection:
docker exec slyber_turn slyber_turn doctor
Check mapped Docker ports on the host:
docker port slyber_turn
Check the binary version inside the image:
docker run --rm ghcr.io/slyber-app/slyber-turn-server:latest --version
Clearing All Keys in Redis Without Stopping Containers
If you need to reset all created accounts in Redis during testing and start with a clean database:
docker exec slyber_redis redis-cli FLUSHALL
Updating via docker run
Pull the latest image version from the registry:
docker pull ghcr.io/slyber-app/slyber-turn-server:latest
Remove the current slyber_turn container and recreate it using the new image:
For secret mode:
SAVED_SECRET=$(docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' slyber_turn 2>/dev/null | grep '^TURN_SECRET=' | cut -d= -f2-)
GEN_SECRET="${SAVED_SECRET:-$(head -c 16 /dev/urandom | od -An -tx1 | tr -d ' \n')}"
docker rm -f slyber_turn 2>/dev/null || true
docker run -d \
--name slyber_turn \
--restart unless-stopped \
-p 3478:3478/udp \
-p 3478:3478/tcp \
-p 8080:8080/tcp \
-p 50000-50100:50000-50100/udp \
-e PUBLIC_IP="auto" \
-e REALM="turn.local" \
-e AUTH_MODE="secret" \
-e TURN_SECRET="${GEN_SECRET}" \
-e RELAY_MIN_PORT=50000 \
-e RELAY_MAX_PORT=50100 \
ghcr.io/slyber-app/slyber-turn-server:latest && \
sleep 1 && \
docker exec slyber_turn slyber_turn --version && \
docker exec slyber_turn slyber_turn doctor && \
HOST_IP=$([ -n "${SSH_CONNECTION:-}" ] && curl -fsSL4 --max-time 2 ifconfig.me 2>/dev/null || echo 127.0.0.1) && \
echo " TURN / STUN URI : turn:${HOST_IP}:3478 (UDP/TCP)" && \
echo " HTTP API URL : http://${HOST_IP}:8080" && \
echo " Realm : turn.local" && \
echo " Auth Mode : secret (TURN_SECRET=${GEN_SECRET})" && \
echo " Test Creds API : curl -s \"http://${HOST_IP}:8080/api/turn-credentials?user_id=dev\" && echo" && \
echo " Run Benchmark : docker exec slyber_turn slyber_turn bench -clients 20 -duration 5s" && \
echo " Live Logs : docker logs -f slyber_turn" && \
echo "--------------------------------------------------"
For redis mode (no need to stop the slyber_redis container, all keys in the database will be preserved):
docker rm -f slyber_turn 2>/dev/null || true
docker run -d \
--name slyber_turn \
--restart unless-stopped \
--network slyber_net \
-p 3478:3478/udp \
-p 3478:3478/tcp \
-p 8080:8080/tcp \
-p 50000-50100:50000-50100/udp \
-e PUBLIC_IP="auto" \
-e REALM="turn.local" \
-e AUTH_MODE="redis" \
-e REDIS_ADDR="slyber_redis:6379" \
-e RELAY_MIN_PORT=50000 \
-e RELAY_MAX_PORT=50100 \
ghcr.io/slyber-app/slyber-turn-server:latest && \
sleep 1 && \
docker exec slyber_turn slyber_turn --version && \
docker exec slyber_turn slyber_turn doctor && \
HOST_IP=$([ -n "${SSH_CONNECTION:-}" ] && curl -fsSL4 --max-time 2 ifconfig.me 2>/dev/null || echo 127.0.0.1) && \
echo " TURN / STUN URI : turn:${HOST_IP}:3478 (UDP/TCP)" && \
echo " HTTP Health URL : http://${HOST_IP}:8080/healthz" && \
echo " Realm : turn.local" && \
echo " Auth Mode : redis (REDIS_ADDR=slyber_redis:6379)" && \
echo " Inspect Keys : docker exec slyber_redis redis-cli KEYS \"turn/realm/*\"" && \
echo " Live Redis Mon : docker exec -it slyber_redis redis-cli MONITOR" && \
echo " Run Benchmark : docker exec slyber_turn slyber_turn bench -clients 20 -duration 5s" && \
echo " Live Logs : docker logs -f slyber_turn" && \
echo "--------------------------------------------------"
Verify that the version has been updated and the server is healthy:
docker exec slyber_turn slyber_turn --version
docker exec slyber_turn slyber_turn doctor
Environment Variables Table
| Variable | Default | Description |
PUBLIC_IP | auto | Automatic detection of the server's external IPv4 address (you can specify 127.0.0.1 for local tests on a single machine) |
REALM | turn.local | Authentication realm of the TURN server |
AUTH_MODE | secret | Authentication mode: secret (HMAC-SHA1) or redis (dynamic keys in the DB) |
TURN_SECRET | — | Secret key used to sign temporary passwords in secret mode |
REDIS_ADDR | 127.0.0.1:6379 | Redis server connection address in redis mode |
RELAY_MIN_PORT | 49152 | Lower bound of the UDP port range for relaying media streams |
RELAY_MAX_PORT | 65535 | Upper bound of the UDP port range for relaying media streams |