Deploying LiveKit with Nginx (Manual Docker Setup)
This guide is for setting up LiveKit with Docker Compose yourself, specifically for integrating with an Nginx reverse proxy you already manage. Compared to the Caddy generator, this gives you more knobs to turn and fits better if Nginx is already part of your workflow for services like Synapse or Continuwuity.
If you haven't already, please review the main LiveKit deployment guide for prerequisites, firewall rules, and DNS configuration before proceeding. Getting those right first will save potential headaches later.
Configuration
- Create Directory: Let's make a dedicated spot for your LiveKit config and data. Something like
/opt/livekit/
is a common choice:mkdir -p /opt/livekit && cd /opt/livekit
-
Generate Keys: We need API keys for LiveKit. Use the
livekit-server
Docker image itself to generate these. Store the output securely, perhaps in your password manager.This will output an
API Key
(e.g.APIp2ow9ec5MJQd
) and anAPI Secret
(e.g.CjwZBVnAeaCVebgtYyWfEjLehqjR6I5PHEkTVdUci2Q
). 3. Createlivekit.yaml
: Create a configuration file/opt/livekit/livekit.yaml
. This file controls the LiveKit server itself. Start with this example, but pay close attention to thertc
,turn
, andkeys
sections./opt/livekit/livekit.yamlport: 7880 bind_addresses: [ 0.0.0.0 ] rtc: tcp_port: 7881 # WebRTC over TCP port_range_start: 50000 # UDP port range for WebRTC port_range_end: 50100 use_external_ip: false # Set to true if not behind NAT/proxy handling external IP turn: enabled: true domain: livekit.your.domain # Must match your domain tls_port: 443 # TURN/TLS will run on the main HTTPS port handled by Nginx udp_port: 3478 # TURN/UDP external_tls: true # Nginx handles TLS termination keys: # Replace API_KEY and API_SECRET with the values generated above API_KEY: API_SECRET # Example: # APIp2ow9ec5MJQd: CjwZBVnAeaCVebgtYyWfEjLehqjR6I5PHEkTVdUci2Q logging: level: info
- Replace
livekit.your.domain
with your actual public domain. - Replace
API_KEY
andAPI_SECRET
with the keys you generated. - Crucially, reduce
port_range_end
(e.g. to50100
for 101 ports) to avoid severe performance degradation when Docker attempts to creates firewall rules for all of the ports. turn.external_tls: true
is crucial here. It tells LiveKit that Nginx (running externally to this container) will handle the TLS termination for TURN connections arriving on port 443, rather than LiveKit trying (and failing) to handle TLS itself when proxied this way.use_external_ip: false
is generally correct when behind a proxy like Nginx.- Create
docker-compose.yaml
: Create/opt/livekit/docker-compose.yaml
to manage the LiveKit server and Redis containers.
/opt/livekit/docker-compose.yamlservices: livekit: command: --config /etc/livekit.yaml deploy: resources: limits: cpus: 2.0 memory: 2G image: livekit/livekit-server:latest network_mode: host restart: unless-stopped volumes: - ./livekit.yaml:/etc/livekit.yaml
- We use
network_mode: host
for optimal performance, particularly for the high-throughput UDP traffic used by WebRTC (ports 50000-50100) and TURN (port 3478). This bypasses potential overhead or limitations associated with Docker's network address translation (NAT) for these port ranges. - Implication: The LiveKit container now directly shares the host's network stack. It will bind directly to the host machine's ports specified in
livekit.yaml
(TCP 7880, 7881, UDP 3478) and the configured UDP range (50000-50100). Ensure these ports are not already in use by other services running directly on the host. - Nginx, running on the same host, can still proxy requests to
127.0.0.1:7880
and127.0.0.1:7881
as LiveKit will be listening on the host's loopback interface.
- Replace
Nginx Configuration
Time to set up Nginx. It needs to act as the front door, handling HTTPS and routing requests to the correct internal LiveKit ports based on the path. Create a new Nginx server block, for example, in /etc/nginx/sites-available/livekit.your.domain.conf
, using SSL/TLS certificates (e.g. from Let's Encrypt via Certbot).
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name livekit.your.domain;
return 301 https://$host$request_uri;
}
# Main LiveKit server block
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name livekit.your.domain;
# SSL Configuration (ensure paths are correct)
ssl_certificate /etc/letsencrypt/live/livekit.your.domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/livekit.your.domain/privkey.pem;
# Include other SSL settings like protocols, ciphers, HSTS as needed
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers '...';
# add_header Strict-Transport-Security "max-age=63072000" always;
# Increase max body size for potential large uploads/data
client_max_body_size 100M;
# Proxy WebSocket connections (LiveKit signaling)
location / {
proxy_pass http://127.0.0.1:7880; # Forward to LiveKit container's port 7880
proxy_set_header Host $http_host; # Use $http_host to preserve port if present
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s; # Allow long-lived connections
}
# Proxy WebRTC/TCP connections (TURN/TLS is also handled here)
# LiveKit uses path prefixes for different protocols on the same port
location /rtc {
proxy_pass http://127.0.0.1:7881; # Forward to LiveKit container's port 7881
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
}
# TURN/TLS requests also arrive here on port 443 but are handled internally by LiveKit
# via the /rtc proxy pass, assuming `turn.external_tls: true` is set in livekit.yaml
}
- Replace
livekit.your.domain
throughout with your actual domain. - Ensure the
ssl_certificate
andssl_certificate_key
paths are correct for your setup. - The
proxy_set_header Upgrade
andproxy_set_header Connection "upgrade"
directives are essential for WebSocket connections used by LiveKit to function correctly through the proxy. - The long
proxy_read_timeout
helps prevent connections from being dropped during long calls. - Enable the site (e.g.
ln -s /etc/nginx/sites-available/livekit.your.domain.conf /etc/nginx/sites-enabled/
) and test the Nginx configuration (nginx -t
). - Reload Nginx:
systemctl reload nginx
.
Starting the Services
- Navigate back to your LiveKit configuration directory:
cd /opt/livekit/
- Start the LiveKit container:
docker compose up -d
- Check the logs to ensure it started correctly:
docker compose logs -f
With the services running and Nginx configured, return to the main LiveKit deployment guide to configure Matrix integration and learn about upgrading and troubleshooting.