Routes auth/chat/files/connect services over Cloudflare Tunnel so your host is never directly exposed on ports 80/443.
LiveKit media still requires direct port forwarding for UDP voice traffic.
Zero Trust IngressLower Host ExposureCloudflare-managed domain required
Requirements (Read First)
Ubuntu host with Docker Engine + Compose plugin.
Cloudflare-managed domain plus Zero Trust access (to create a tunnel token).
Router access for LiveKit media port forwarding (44381/tcp+udp, 44382/udp).
Public IPv4 (or ISP config) for reliable media. CGNAT can break voice.
docker --version
docker compose version
curl -4 ifconfig.me
How This Path Works
Cloudflare Tunnel proxies HTTP/WebSocket services (auth/chat/files/connect) from Cloudflare to your Docker network.
LiveKit media is different: raw UDP/TCP media must still be reachable directly on your host via router forwarding.
Step 0: Get Shared Files
Download the shared base compose file and LiveKit config:
lk.example.com → A record pointing to your host public IP (media is not tunnelled)
Copy the tunnel token from the Zero Trust dashboard — you will need it in the next step.
Step 3: Configure Secrets and Endpoints
Open .env and replace every placeholder. Generate secrets with:
openssl rand -hex 32 # for AUTH_JWT_SECRET, MINIO_SECRET_KEY
openssl rand -base64 32 # for LIVEKIT_API_SECRET
Key fields to fill in:
AUTH_JWT_SECRET= # generate with openssl
LIVEKIT_API_KEY=letschat-prod
LIVEKIT_API_SECRET= # generate with openssl
MINIO_ACCESS_KEY= # any username you choose
MINIO_SECRET_KEY= # generate with openssl
MINIO_PUBLIC_ENDPOINT=https://files.example.com
DISCOVERY_SPACETIMEDB_URI=wss://chat.example.com
DISCOVERY_AUTH_URL=https://auth.example.com
DISCOVERY_LIVEKIT_URL=ws://lk.example.com:44380
CLOUDFLARE_TUNNEL_TOKEN= # from Zero Trust dashboard
Step 4: Configure LiveKit
Open livekit/config.prod.yaml and replace the key/secret placeholder so it matches your .env: