Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/fluxerapp/fluxer/llms.txt

Use this file to discover all available pages before exploring further.

Fluxer uses LiveKit as its WebRTC Selective Forwarding Unit (SFU) for voice and video calls. This guide covers deployment, configuration, and troubleshooting.

Overview

LiveKit provides:

Voice Channels

Multi-participant voice channels with spatial audio, noise suppression, and echo cancellation.

Video Calls

HD video with simulcast, screen sharing, and adaptive bitrate.

TURN/STUN

NAT traversal with built-in TURN server for users behind restrictive firewalls.

Recording

Optional call recording and egress to S3-compatible storage.

Deployment Options

You can deploy LiveKit in several ways:

LiveKit Configuration

Create config/livekit.yaml for self-hosted deployments:
config/livekit.yaml
port: 7880

# API Keys (generate with `livekit-server generate-keys`)
keys:
  APIxxxxxxxxx: secretxxxxxxxxxxxxxxxxx

rtc:
  # Port range for WebRTC connections
  port_range_start: 50000
  port_range_end: 50100
  
  # TCP fallback port
  tcp_port: 7881
  
  # Use external IP for clients (auto-detected if not set)
  # use_external_ip: true
  # node_ip: 203.0.113.42

turn:
  enabled: true
  
  # TURN server for NAT traversal
  udp_port: 3478
  
  # External TURN server (optional)
  # external_tls: true

room:
  # Automatically create rooms when clients connect
  auto_create: true
  
  # Maximum participants per room
  max_participants: 100
  
  # Timeout before empty rooms are deleted (seconds)
  empty_timeout: 300
  
  # Enable room departure timeout
  departure_timeout: 20

# Webhook for events (participant joined, track published, etc.)
webhook:
  api_key: webhookxxxxxxxxxxxxx
  urls:
    - https://chat.example.com/api/webhooks/livekit

# Logging
logging:
  level: info
  # sample: true  # Enable to reduce log volume

# Redis for distributed deployments (optional)
# redis:
#   address: redis:6379
#   db: 0

# Recording (optional)
# egress:
#   s3:
#     access_key: your-s3-key
#     secret: your-s3-secret
#     endpoint: s3.amazonaws.com
#     bucket: livekit-recordings

Generate API Keys

If not using livekitctl, generate keys manually:
docker run --rm livekit/livekit-server:v1.9.11 generate-keys
Output:
API Key: APIxxxxxxxxx
API Secret: secretxxxxxxxxxxxxxxxxx

Fluxer Configuration

Update your config/config.json to enable voice:
config/config.json
{
  "integrations": {
    "voice": {
      "enabled": true,
      
      // API credentials from livekit.yaml
      "api_key": "APIxxxxxxxxx",
      "api_secret": "secretxxxxxxxxxxxxxxxxx",
      
      // WebSocket URL for clients (wss:// in production)
      "url": "wss://chat.example.com/livekit",
      
      // Webhook endpoint for LiveKit events
      "webhook_url": "https://chat.example.com/api/webhooks/livekit",
      
      // Default voice region (optional)
      "default_region": {
        "id": "default",
        "name": "Default Region",
        "emoji": "🌐",
        "latitude": 40.7128,
        "longitude": -74.0060
      }
    }
  }
}
url
string
required
LiveKit WebSocket URL that clients connect to. Must be publicly accessible.
  • Self-hosted: wss://livekit.example.com or wss://chat.example.com/livekit
  • LiveKit Cloud: wss://your-project.livekit.cloud
  • Development: ws://localhost:7880 (HTTP OK for localhost)
webhook_url
string
Endpoint where LiveKit sends event webhooks. Used to sync room state and participant changes.
default_region
object
If specified, Fluxer automatically creates this voice region on startup if none exist. Useful for single-region deployments.

Network Configuration

Required Ports

LiveKit requires these ports to be accessible from the internet:
PortProtocolPurposeFirewall Rule
7880TCPHTTP/WebSocket (internal)Not exposed (behind reverse proxy)
7881TCPRTC over TCP (fallback)ufw allow 7881/tcp
3478UDPTURN/STUNufw allow 3478/udp
50000-50100UDPRTP/RTCP media streamsufw allow 50000:50100/udp
Critical: UDP ports cannot be proxied through Cloudflare Tunnels, ngrok, or similar HTTP-only tunnels. They must be directly accessible via the server’s public IP.

Firewall Configuration

# Allow HTTPS (for WebSocket signaling via reverse proxy)
sudo ufw allow 443/tcp

# Allow LiveKit UDP ports
sudo ufw allow 7881/tcp
sudo ufw allow 3478/udp
sudo ufw allow 50000:50100/udp

sudo ufw enable

Reverse Proxy Configuration

Proxy the LiveKit WebSocket endpoint through your existing reverse proxy:
/etc/nginx/sites-available/fluxer
server {
    listen 443 ssl http2;
    server_name chat.example.com;
    
    # ... existing Fluxer config ...
    
    # LiveKit WebSocket proxy
    location /livekit {
        proxy_pass http://127.0.0.1:7880;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Disable buffering for WebSocket
        proxy_buffering off;
        
        # Increase timeouts
        proxy_read_timeout 3600;
        proxy_send_timeout 3600;
    }
}

DNS Configuration

If using a dedicated subdomain for LiveKit:
A    livekit.chat.example.com  →  203.0.113.42
AAAA livekit.chat.example.com  →  2001:db8::1
Then configure LiveKit to advertise this domain:
livekit.yaml
rtc:
  use_external_ip: false  # Use domain instead of IP
  # Clients will connect to livekit.chat.example.com

Multi-Region Voice

For global deployments, deploy multiple LiveKit servers in different regions.

1. Deploy Regional LiveKit Servers

Deploy LiveKit in each region (e.g., us-east, eu-west, ap-south):
# Region: US East (Virginia)
sudo livekitctl bootstrap \
  --livekit-domain livekit-us.chat.example.com \
  --turn-domain turn-us.chat.example.com \
  --email admin@example.com

# Region: EU West (Ireland)  
sudo livekitctl bootstrap \
  --livekit-domain livekit-eu.chat.example.com \
  --turn-domain turn-eu.chat.example.com \
  --email admin@example.com

2. Add Regions in Fluxer Admin Panel

Navigate to Admin Panel → Voice → Regions and add:
[
  {
    "id": "us-east",
    "name": "US East",
    "emoji": "🇺🇸",
    "latitude": 37.7749,
    "longitude": -122.4194,
    "servers": [
      {
        "url": "wss://livekit-us.chat.example.com",
        "api_key": "APIxxxxxxxxx",
        "api_secret": "secretxxxxxxxxxxxxxxxxx"
      }
    ]
  },
  {
    "id": "eu-west",
    "name": "EU West",
    "emoji": "🇪🇺",
    "latitude": 53.3498,
    "longitude": -6.2603,
    "servers": [
      {
        "url": "wss://livekit-eu.chat.example.com",
        "api_key": "APIxxxxxxxxx",
        "api_secret": "secretxxxxxxxxxxxxxxxxx"
      }
    ]
  }
]
Fluxer automatically selects the closest region based on user latency.

Monitoring

LiveKit Logs

sudo livekitctl logs --service livekit.service --lines 200

# Follow logs in real-time
sudo journalctl -u livekit.service -f

Health Checks

# LiveKit health endpoint
curl http://localhost:7880/

# Check if ports are listening
ss -tuln | grep -E '7880|7881|3478|50000'

# Test UDP connectivity (from client)
nc -u -v -w 1 chat.example.com 3478

Metrics

LiveKit exposes Prometheus metrics at http://localhost:7880/metrics:
prometheus.yml
scrape_configs:
  - job_name: 'livekit'
    static_configs:
      - targets: ['localhost:7880']
Key metrics:
  • livekit_room_total - Active rooms
  • livekit_participant_total - Connected participants
  • livekit_track_published_total - Published audio/video tracks
  • livekit_packet_total - RTP packets sent/received

Troubleshooting

  1. Verify voice is enabled in config:
    grep -A 5 '"voice"' config/config.json
    
  2. Check Fluxer logs for LiveKit connection errors:
    docker compose logs fluxer_server | grep -i livekit
    
  3. Ensure api_key and api_secret match between livekit.yaml and Fluxer config.
Symptoms: “Connecting…” spinner never completes
  1. Check if LiveKit is accessible:
    curl -I https://chat.example.com/livekit
    # Should return 404 or 426 (Upgrade Required)
    
  2. Verify WebSocket URL in config matches your reverse proxy path.
  3. Check browser console for WebSocket errors (F12 → Console).
  4. Test with LiveKit’s CLI:
    livekit-cli join-room \
      --url wss://chat.example.com/livekit \
      --api-key APIxxxxxxxxx \
      --api-secret secretxxxxxxxxxxxxxxxxx \
      --room test-room \
      --identity test-user
    
Cause: UDP ports blocked or TURN not working
  1. Verify UDP ports are open:
    sudo ufw status | grep -E '3478|7881|50000'
    
  2. Test TURN server:
    # Install trickle-ice
    npm install -g trickle-ice
    
    # Test TURN connectivity
    trickle-ice chat.example.com 3478
    
  3. Check LiveKit logs for ICE connection failures:
    sudo journalctl -u livekit.service | grep -i 'ice\|turn'
    
  4. Ensure TURN is enabled in livekit.yaml:
    turn:
      enabled: true
      udp_port: 3478
    
  1. Enable echo cancellation in LiveKit:
    audio:
      echo_cancellation: true
      noise_suppression: true
      auto_gain_control: true
    
  2. Ask users to use headphones or reduce speaker volume.
  3. Check if multiple clients are running on the same device.
  1. Check number of concurrent rooms/participants:
    curl -s http://localhost:7880/metrics | grep livekit_room_total
    
  2. Consider scaling horizontally (add more LiveKit nodes).
  3. Reduce video quality in client settings:
    • Lower resolution (720p → 480p)
    • Reduce framerate (30fps → 15fps)
    • Disable simulcast for low-end clients

Advanced Configuration

Custom Codecs

livekit.yaml
audio:
  # Prefer Opus codec
  codecs:
    - opus

video:
  # Use VP8 (better compatibility) or VP9 (better compression)
  codecs:
    - vp8
    - h264

Recording to S3

livekit.yaml
egress:
  s3:
    access_key: your-s3-access-key
    secret: your-s3-secret-key
    endpoint: s3.amazonaws.com
    bucket: livekit-recordings
    region: us-east-1
Trigger recording via API:
curl -X POST https://chat.example.com/api/v1/channels/:id/record \
  -H "Authorization: Bearer your-token"

Redis for Distributed LiveKit

For multi-node LiveKit deployments, use Redis for state synchronization:
livekit.yaml
redis:
  address: redis:6379
  db: 0
  username: default
  password: your-redis-password
All LiveKit nodes must share the same Redis instance.

Next Steps

Scaling Guide

Scale voice infrastructure globally with multi-region deployments

Architecture

Understand how LiveKit integrates with Fluxer