The simplest production layout puts the client and the signaling server behind
one domain, so there is a single certificate and a single URL to configure.
See Reverse Proxy for copy-pasteable Caddy and
nginx configuration. This page covers the two-subdomain layout, which is still
fully supported.
Steps
1
Put both services behind a reverse proxy with HTTPS
Browsers require a secure context (HTTPS) for WebRTC and the Screen Wake Lock API. Use Nginx, Caddy, Traefik, or a managed platform like Render or Fly.Proxy each service to a hostname you control:
app.your-domain.comto the client on:3000api.your-domain.comto the signaling server on:3001
Upgrade and Connection headers.2
Set NEXT_PUBLIC_SOCKET_URL
In Then recreate the containers. There is no rebuild: Compose maps this value onto the client container’s
.env:.env
SOCKET_URL, and the browser reads it from GET /api/config on every page load.Terminal
3
Set CLIENT_URL
In This adds your client origin to the server’s CORS allow-list. Without it, browser requests from your domain will be blocked.
.env:.env
4
Set TRUSTED_PROXY_COUNT
Set this to the number of proxy hops in front of the signaling server so per-IP rate limiting reads real client IPs instead of proxy IPs:One reverse proxy is
.env
1, a CDN in front of that proxy is 2. The server’s own default is 1; Docker Compose passes 0 explicitly, which is correct only when the container ports are published directly. Setting it higher than your real hop count lets clients forge X-Forwarded-For and bypass the per-IP rate limits, so if you expose the server directly with no proxy, set 0 deliberately rather than leaving it unset.5
Point the CLI and the desktop app at your instance
A split deployment has two addresses, so both clients need both. The CLI takes them as flags, or as Without
FLOE_SERVER and FLOE_WEB in the environment:Terminal
--web, the share link points at the signaling API rather than the web app, because a self-hosted server is otherwise assumed to serve both from one origin.In the desktop app, open Settings, expand Advanced, and fill in both Server address (https://api.your-domain.com) and Share link address (https://app.your-domain.com), then press Test. See Desktop Settings.CORS note
The server’s allow-list also includes the officialfloe.one origins (https://floe.one and https://www.floe.one) plus http://localhost:3000 for local development. Your CLIENT_URL is added on top of these, so browser clients on your domain and on floe.one can both reach your server. If you are running a private fork and want a strict allow-list, remove the hard-coded entries from server/server.js.