Reverse Proxy Setup
Put Arcane behind Nginx, Apache, or Traefik with WebSockets and the real client IP.
To run Arcane behind Nginx, Apache, Traefik, or another reverse proxy, the proxy must forward WebSockets (used for live updates) and the real client IP (for per-client login rate limits). Set ARCANE_TRUSTED_PROXIES so Arcane believes that IP, and keep the proxy’s header-size limit big enough for session cookies. Set ARCANE_APP_URL to the public URL users open, such as https://arcane.example.com, without the internal :3552 port.
Configure your proxy
Section titled “Configure your proxy”This example enables WebSockets. Replace the domain name and certificate paths with your own:
server { listen 80; server_name arcane.yourdomain.com; return 301 https://$host$request_uri;}
server { listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/arcane.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/arcane.yourdomain.com/privkey.pem;
server_name arcane.yourdomain.com;
location / { add_header X-Robots-Tag "noindex, nofollow"; proxy_pass http://127.0.0.1:3552; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $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_cache_bypass $http_upgrade; }
access_log /var/log/nginx/arcane-access.log; error_log /var/log/nginx/arcane-error.log debug;}The proxy_http_version 1.1;, Upgrade, Connection "upgrade", and proxy_cache_bypass lines enable WebSockets. The client IP goes in X-Forwarded-For and X-Real-IP; set ARCANE_TRUSTED_PROXIES so Arcane trusts it.
On Apache 2.4.47 or later, use mod_proxy_http and a ProxyPassMatch rule with upgrade=websocket:
Define HOST arcane.example.comDefine PORT 3552
<VirtualHost *:443> ServerName ${HOST}
ProxyPassMatch ^/(.*)\/ws\/(.*)$ ws://127.0.0.1:${PORT}/$1/ws/$2 upgrade=websocket ProxyPass / http://127.0.0.1:${PORT}/ ProxyPassReverse / http://127.0.0.1:${PORT}/
ErrorLog ${APACHE_LOG_DIR}/arcane.error.log CustomLog ${APACHE_LOG_DIR}/arcane.access.log combined
Include /etc/letsencrypt/options-ssl-apache.conf SSLCertificateFile /etc/letsencrypt/live/${HOST}/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/${HOST}/privkey.pem</VirtualHost>mod_proxy adds X-Forwarded-For automatically; set ARCANE_TRUSTED_PROXIES so Arcane trusts it.
Traefik forwards WebSockets without extra middleware. Edge Agents with ARCANE_EDGE_TRANSPORT=auto (the default) or grpc open a long-lived gRPC tunnel to /api/tunnel/connect, so Traefik must forward that path as unencrypted HTTP/2 (h2c) without timing it out. Without Edge Agents, you only need the web router in step 2.
1. Turn off the HTTPS read timeout
Section titled “1. Turn off the HTTPS read timeout”Traefik’s HTTPS entrypoint times out after 60 seconds by default, killing the gRPC stream. Add this to the Traefik command (or static config). It goes on the entrypoint, not a router. See Traefik’s responding timeouts.
command: - '--entrypoints.websecure.transport.respondingtimeouts.readtimeout=0s'2. Add gRPC and web routers
Section titled “2. Add gRPC and web routers”Put these labels on your Arcane service. Change arcane.example.com, letsencrypt, and proxy to your hostname, certificate resolver, and Traefik’s Docker network. ARCANE_TRUSTED_PROXIES is that network’s subnet (how to look it up).
services: arcane: image: ghcr.io/getarcaneapp/manager:latest environment: - ARCANE_APP_URL=https://arcane.example.com - ARCANE_TRUSTED_PROXIES=172.18.0.0/16 networks: - proxy labels: - traefik.enable=true - traefik.docker.network=proxy - 'traefik.http.routers.arcane-grpc.rule=Host(`arcane.example.com`) && Path(`/api/tunnel/connect`) && Method(`POST`) && HeaderRegexp(`Content-Type`, `^application/grpc`)' - traefik.http.routers.arcane-grpc.entrypoints=websecure - traefik.http.routers.arcane-grpc.priority=100 - traefik.http.routers.arcane-grpc.tls.certresolver=letsencrypt - traefik.http.routers.arcane-grpc.service=arcane-grpc - traefik.http.services.arcane-grpc.loadbalancer.server.port=3552 - traefik.http.services.arcane-grpc.loadbalancer.server.scheme=h2c - 'traefik.http.routers.arcane-web.rule=Host(`arcane.example.com`)' - traefik.http.routers.arcane-web.entrypoints=websecure - traefik.http.routers.arcane-web.priority=10 - traefik.http.routers.arcane-web.tls.certresolver=letsencrypt - traefik.http.routers.arcane-web.service=arcane-web - traefik.http.services.arcane-web.loadbalancer.server.port=3552 - traefik.http.services.arcane-web.loadbalancer.server.scheme=http
networks: proxy: external: trueThe gRPC router takes POST /api/tunnel/connect; everything else, including WebSockets, uses the web router. See Traefik’s exposing gRPC for more on h2c backends.
3. Point the Edge Agent at that URL
Section titled “3. Point the Edge Agent at that URL”ARCANE_MANAGER_API_URL should match ARCANE_APP_URL. See Remote Environments if you haven’t created the agent yet.
environment: - ARCANE_EDGE_AGENT=true - ARCANE_EDGE_TRANSPORT=auto - ARCANE_MANAGER_API_URL=https://arcane.example.com - ARCANE_AGENT_TOKEN=arc_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4. Restart and check the agent logs
Section titled “4. Restart and check the agent logs”Restart Traefik and the Edge Agent, then read the agent’s logs. Success logs Edge tunnel connected to manager with transport=grpc. If Traefik doesn’t forward gRPC, the agent logs gRPC edge tunnel connection failed, falling back to websocket transport and uses WebSocket.
docker restart traefik arcane-edge-agentdocker logs -f arcane-edge-agentTrust the proxy with ARCANE_TRUSTED_PROXIES
Section titled “Trust the proxy with ARCANE_TRUSTED_PROXIES”Set ARCANE_TRUSTED_PROXIES on the Arcane container to your proxy’s address (ARCANE_TRUSTED_PROXIES=10.0.0.5/32) or a wider CIDR range, such as the shared Docker network (ARCANE_TRUSTED_PROXIES=172.18.0.0/16). Arcane then reads the real client IP from X-Forwarded-For for requests from those addresses. Without it, auth endpoints (login, token refresh, OIDC callback) rate-limit by the proxy’s IP, weakening brute-force protection and locking out users who share the proxy.
Docker’s default pools use 172.17.0.0–172.31.0.0, then 192.168.0.0/16, and your daemon may differ, so look up the actual subnet:
docker network inspect <network> --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'ARCANE_TRUSTED_PROXIES accepts a comma-separated list of CIDR ranges. Write a single address as /32 for IPv4 or /128 for IPv6; Arcane logs a warning and ignores entries without a prefix length. Forwarded headers are trusted only when the direct peer is listed, so untrusted clients can’t spoof X-Forwarded-For. Loopback and private addresses aren’t trusted unless listed. See Environment Variables for all options.
Allow large enough headers
Section titled “Allow large enough headers”If sign-in fails with oversized-header errors, raise your proxy’s request header limit, such as large_client_header_buffers in Nginx. Current sessions fit in one cookie, but sessions from older releases carry larger ML-DSA-87 (post-quantum) tokens split across up to four cookies until they refresh.
Other proxies
Section titled “Other proxies”websocket.org has WebSocket guides for other proxies and load balancers: Nginx, Amazon ALB, Cloudflare, and Kubernetes.