Fluxer self-hosted deployment guide (refactor branch) — 20 gotchas documented This article provides a deployment guide for Fluxer, a self-hosted, open-source Discord alternative, detailing 20 common issues ("gotchas") encountered during setup. Key recommendations include keeping Cloudflare proxy enabled for TLS termination, disabling specific Cloudflare optimizations like Rocket Loader and JavaScript minification to avoid CSP errors, and building the Docker image from source due to a private registry. The guide also specifies critical configuration settings, such as using absolute paths for SQLite and static assets, and notes that while Cloudflare handles WebSocket signaling for LiveKit, direct UDP/TCP media transport bypasses the proxy entirely. Fluxer Self-Hosted Deployment Guide Deployment log for Fluxer on fluxer.mydomain.net — a free, open-source Discord alternative AGPLv3 . Architecture Fluxer runs as a monolith Docker Compose stack with 6 containers: | Container | Image | Purpose | |---|---|---| | fluxer-server | Built from source | API, WebSocket gateway Erlang/OTP , media proxy, admin panel, SPA web app | | fluxer-valkey | valkey/valkey:8.0.6-alpine | Redis-compatible cache/session store | | fluxer-meilisearch | getmeili/meilisearch:v1.14 | Full-text search engine | | fluxer-livekit | livekit/livekit-server:v1.9.11 | Voice/video SFU WebRTC | | fluxer-nats-core | nats:2-alpine | Pub/sub messaging | | fluxer-nats-jetstream | nats:2-alpine | Persistent job queue | Tech stack: Node.js/TypeScript backend, Erlang/OTP WebSocket gateway, React/Rust-WASM frontend, SQLite database. Prerequisites - Docker + Docker Compose - Traefik reverse proxy with TLS certresolver or Cloudflare-terminated - External proxy Docker network - Two DNS records pointing to your server - SMTP relay accessible on the proxy network optional, for email Cloudflare DNS and TLS TL;DR: Keep Cloudflare proxy enabled orange cloud for both fluxer and lk records. Do NOT set DNS-only grey cloud unless you have your own TLS certificates. Why Cloudflare proxy is required If your Traefik setup relies on Cloudflare for TLS termination SSL mode "Full" with Cloudflare's edge cert , then switching to DNS-only grey cloud will break HTTPS. Clients will receive Traefik's default self-signed certificate, causing browser security errors. This is the case when acme.json contains no certificates and Traefik uses its default cert — Cloudflare's "Full" SSL mode accepts any origin cert including self-signed , so it works transparently when proxied. Cloudflare proxy does NOT cause CSP errors If you see Content-Security-Policy errors in the browser console, they are not caused by Cloudflare proxy. Common CSP errors and their real causes: | Error | Real Cause | Fix | |---|---|---| | script-src-elem blocking inline script from single-file-extension-frames.js | Browser extension SingleFile, etc. | Not a Fluxer issue — disable the extension or ignore | | style-src-elem blocking fluxerstatic.com/fonts/ibm-plex.css | Server CSP doesn't include fluxerstatic.com | See Gotcha 11 below | | img-src blocking fluxerstatic.com/web/ .png | Server CSP doesn't include fluxerstatic.com | See Gotcha 11 below | | connect-src blocking chat.example.com/.well-known/fluxer | Frontend built with wrong domain | See Gotcha 12 below | Cloudflare settings to verify If you use Cloudflare proxy, ensure these settings in the Cloudflare dashboard: - SSL/TLS mode : Full not Flexible, not Full Strict - Speed Optimization Auto Minify : Disable JavaScript minification can break hashed assets - Speed Optimization Rocket Loader : OFF injects scripts that may conflict with CSP nonces - Scrape Shield Email Address Obfuscation : OFF injects inline scripts LiveKit media transport Cloudflare proxy handles HTTP/WebSocket signaling for LiveKit lk.yourdomain.com . The actual voice/video media transport RTP uses direct UDP/TCP connections on ports 7881, 3478, and 50000-50100, which bypass DNS entirely — clients connect to the server's IP directly via ICE/STUN negotiation. Cloudflare proxy does not interfere with this. Step 1: Clone the Source bash mkdir -p /srv/fluxer cd /srv/fluxer git clone https://github.com/fluxerapp/fluxer.git cd fluxer git checkout refactor The self-hosting/monolith branch Gotcha: No public Docker image. The GHCR image at ghcr.io/fluxerapp/fluxer-server:stable requires authentication private registry . You must build from source. Step 2: Generate Secrets Generate hex secrets for all config values: bash 64-char hex strings 32 bytes openssl rand -hex 32 Repeat for each secret below 16-char hex string for LiveKit API key openssl rand -hex 8 Create /srv/fluxer/.env : env MEILI MASTER KEY=<64-char hex FLUXER SERVER IMAGE=fluxer-server:local bash chmod 600 /srv/fluxer/.env Step 3: Create Config Files /srv/fluxer/config/config.json Key settings to get right: jsonc { "env": "production", "domain": { "base domain": "fluxer.yourdomain.com", "public scheme": "https", "public port": 443 }, "database": { "backend": "sqlite", "sqlite path": "/usr/src/app/data/fluxer.db" // CRITICAL - must be absolute, see Gotcha 14 }, "internal": { "kv": "redis://fluxer-valkey:6379/0", "kv mode": "standalone" }, "s3": { "access key id": "fluxer-local", "secret access key": "fluxer-local-secret", "endpoint": "http://127.0.0.1:8080/s3" // Built-in local S3 }, "instance": { "self hosted": true, "deployment mode": "monolith" }, "services": { "server": { "port": 8080, "host": "0.0.0.0", "static dir": "/usr/src/app/assets" // CRITICAL - see Gotcha 5 }, "gateway": { "port": 8082 }, "nats": { "core url": "nats://fluxer-nats-core:4222", "jetstream url": "nats://fluxer-nats-jetstream:4223", "auth token": "" } // ... media proxy, admin, marketing with their secrets }, "integrations": { "search": { "engine": "meilisearch", "url": "http://fluxer-meilisearch:7700", "api key": "