Skip to content

Reverse proxy & TLS ​

Refract.Chat doesn't terminate TLS itself. Put a reverse proxy such as Traefik, Caddy or nginx in front of it, with two host names:

Host nameProxies toCarries
chat.example.combackend:8778 (HTTP)The web app, the API, Socket.IO, media, activities and federation
livekit.chat.example.comlivekit:7866 (HTTP)LiveKit's signalling, over WebSocket

Both need WebSocket support: Socket.IO lives at /api/socket.io, and LiveKit signals over a WebSocket.

Ports ​

PortServicePublish it?
8778/tcpbackendOnly through the proxy, with TLS
8779/tcpbackendNever. LiveKit's webhooks arrive here, and only LiveKit should reach it, over the Docker network
7866/tcpLiveKitOnly through the proxy, with TLS
7867/udpLiveKitDirectly, not through the proxy: call media
7868/tcpLiveKitDirectly, not through the proxy: call media when UDP is blocked

The media ports must match LiveKit's own configuration. See Voice & LiveKit to change them.

Traefik ​

docker-compose.override.example.yaml has working Traefik labels for both services. Copy it to docker-compose.override.yaml and change the host names, the entry point (https) and the certificate resolver (refract_chat) to your Traefik's. Both containers join the external traefik-net network so Traefik can reach them.

nginx ​

The same routes for nginx, with your own certificates:

nginx
map $http_upgrade $connection_upgrade {
  default upgrade;
  ""      close;
}

server {
  listen 443 ssl;
  server_name chat.example.com;

  # allow the instance's file size limit (100 MB unless an admin changed it)
  client_max_body_size 100m;

  location / {
    proxy_pass http://127.0.0.1:8778;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

server {
  listen 443 ssl;
  server_name livekit.chat.example.com;

  location / {
    proxy_pass http://127.0.0.1:7866;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
  }
}

This assumes nginx runs on the host, so publish 8778 and 7866 on 127.0.0.1 only, in your docker-compose.override.yaml.

Upload size ​

The proxy must accept files up to the instance's limit, which is 100 MB unless an admin changes it under Manage instance → Config → Instance features. A proxy with a lower limit wins, and uploads above it fail before they reach Refract.

Client IP addresses ​

Behind a proxy, every request seems to come from the proxy, so rate limits and IP bans would apply to everyone at once. Tell the server which proxy to trust, in .env:

dotenv
TRUST_PROXY=true
TRUST_PROXY_IPS=172.20.0.0/16

TRUST_PROXY_IPS takes addresses or CIDR ranges, comma-separated. A container's address can change when it's recreated, so trusting the proxy's Docker network is simplest. For Traefik, that's the traefik-net subnet:

sh
docker network inspect traefik-net --format '{{(index .IPAM.Config 0).Subnet}}'

Only trust a network that nothing else can use to reach the server directly: anything trusted can claim to be any client. The server reads X-Forwarded-For (or TRUST_PROXY_HEADER, if set), and walks it from the right past every trusted hop to the real client. Its startup log shows what it trusts in a Client IPs: line, and it warns once for each untrusted peer that sends a forwarded header.

Several server processes ​

One server container runs one API process, which needs nothing special. If you run several behind a load balancer, they must share the database, Redis and JWT_SECRET, and the load balancer needs sticky sessions, because Socket.IO falls back to HTTP long-polling, which must keep hitting the same process.

Released under the GNU Affero General Public License v3.0.