Jun 14, 2026

Self-Hosting LiveKit on a VPS

This guide covers setting up a LiveKit server on an Ubuntu VPS for use with DEN Chat voice channels. LiveKit includes a built-in TURN relay, so no separate TURN server (coturn, etc.) is needed.

This guide covers setting up a LiveKit server on an Ubuntu VPS for use with DEN Chat voice channels. LiveKit includes a built-in TURN relay, so no separate TURN server (coturn, etc.) is needed.

Requirements:

  • Ubuntu VPS (BitLaunch, Hetzner, etc.)
  • A domain name pointed at your server (Let's Encrypt does not issue certs for bare IPs)
  • Root/sudo access

1. Provision your VPS

Minimum specs (testing)

  • 1 vCPU, 1 GB RAM, 25 GB SSD

Recommended specs (production)

  • 2-4 vCPU, 4-8 GB RAM

WebRTC is network-heavy, not disk-heavy. Prioritize bandwidth over storage.


2. Point a domain at your server

You need a domain because browsers block insecure WebSocket connections (ws://) from HTTPS pages. TLS requires a certificate, and Let's Encrypt does not issue certificates for bare IP addresses.

If you don't have a domain, free options work fine:

  • DuckDNS — free subdomains like yourname.duckdns.org
  • FreeDNS (afraid.org) — free subdomains on various domains

In your DNS provider, create an A record pointing to your server's IP:

TypeNameValue
AlkYOUR_SERVER_IP

This gives you lk.yourdomain.com. DNS propagation can take a few minutes to a few hours. Verify it's ready before continuing:

nslookup lk.yourdomain.com

You should see your server's IP in the response. If not, wait and try again.


3. Connect and install dependencies

ssh root@YOUR_SERVER_IP
apt update && apt upgrade -y
apt install -y curl openssl ufw

4. Install LiveKit Server

curl -sSL https://get.livekit.io | bash

Verify:

livekit-server --version

5. Generate an API key and secret

openssl rand -hex 32

Save the output — this is your API secret. Choose an API key name (any string, e.g. myapikey).


6. Create the LiveKit config

nano livekit.yaml
port: 7880

rtc:
  tcp_port: 7881
  port_range_start: 50000
  port_range_end: 60000
  use_ice_lite: true
  use_external_ip: true

turn:
  enabled: true
  domain: lk.yourdomain.com
  udp_port: 3478
  tls_port: 0

keys:
  myapikey: YOUR_SECRET_HERE

Replace lk.yourdomain.com with your actual domain, myapikey with your key name, and YOUR_SECRET_HERE with the secret from step 5.

The turn block enables LiveKit's built-in TURN relay over UDP. No external TURN server is needed. WebSocket encryption (WSS) is handled by Caddy separately.


7. Install Caddy (TLS reverse proxy)

Caddy provides automatic HTTPS with Let's Encrypt — no manual certificate management.

apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update
apt install caddy

Create the Caddy config:

nano /etc/caddy/Caddyfile
lk.yourdomain.com {
    reverse_proxy localhost:7880
}

Replace lk.yourdomain.com with your domain. Start Caddy:

systemctl restart caddy

Caddy will automatically obtain and renew the TLS certificate.


8. Open firewall ports

ufw allow 22/tcp       # SSH
ufw allow 80/tcp       # Caddy (Let's Encrypt HTTP challenge)
ufw allow 443/tcp      # Caddy (WSS)
ufw allow 7881/tcp     # RTC over TCP
ufw allow 3478/udp     # TURN (UDP)
ufw allow 50000:60000/udp  # WebRTC media
ufw enable

9. Start LiveKit

Test run (foreground):

livekit-server --config livekit.yaml

Leave it running and open a second SSH session to your server. Verify locally:

curl http://127.0.0.1:7880

Expected output: OK

Verify through Caddy:

curl https://lk.yourdomain.com

Should also return OK. If it does, TLS is working.

Once verified, go back to the first SSH session and press Ctrl+C to stop the foreground process.


10. Run LiveKit as a service

LiveKit needs to run permanently in the background — surviving SSH disconnects and server reboots. Create a systemd service for this:

nano /etc/systemd/system/livekit.service

Paste the following:

[Unit]
Description=LiveKit Server
After=network.target

[Service]
ExecStart=/usr/local/bin/livekit-server --config /root/livekit.yaml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Save and exit (Ctrl+X, then Y, then Enter). Then enable and start it:

systemctl enable livekit
systemctl start livekit

Verify it's running:

systemctl status livekit

You should see active (running). From now on, LiveKit starts automatically on boot and restarts if it crashes.


11. Configure DEN Chat

In your hub settings, set the voice provider to LiveKit and enter:

FieldValue
Server URLwss://lk.yourdomain.com
API Keymyapikey
API SecretThe secret from step 5

DEN Chat handles token generation and TURN relay configuration automatically.


12. Verify

LiveKit server:

curl https://lk.yourdomain.com

Should return OK.

Voice channel: Join a voice channel in DEN Chat. If you hear audio, it's working.

TURN relay: Confirm via chrome://webrtc-internals — look for relay candidates in the ICE connection log after joining a voice channel.


Rotating credentials

If a member leaves your hub and you want to prevent them from using your LiveKit server, rotate your API secret:

  1. Generate a new secret:
openssl rand -hex 32
  1. Update livekit.yaml with the new secret:
nano livekit.yaml

Replace the old secret in the keys: section with the new one. Save and exit (Ctrl+X, then Y, then Enter).

  1. Restart LiveKit:
systemctl restart livekit
  1. Update your DEN Chat hub settings with the new API secret.

The old secret is immediately invalidated. Any tokens generated with the old secret will be rejected.