- Clone of github.com/fluxerapp/fluxer (official upstream) - SELF_HOSTING.md: full VM rebuild procedure, architecture overview, service reference, step-by-step setup, troubleshooting, seattle reference - dev/.env.example: all env vars with secrets redacted and generation instructions - dev/livekit.yaml: LiveKit config template with placeholder keys - fluxer-seattle/: existing seattle deployment setup scripts
385 lines
11 KiB
Markdown
385 lines
11 KiB
Markdown
# Fluxer — Self-Hosting & VM Rebuild Guide
|
|
|
|
This document covers how to spin up the Fluxer dev instance from scratch on a new VM.
|
|
The running reference deployment is at `st.vish.gg` (seattle VM, `66.94.122.170` / `100.82.197.124`).
|
|
|
|
---
|
|
|
|
## Architecture overview
|
|
|
|
All services run as Docker containers via `dev/compose.yaml`, connected on a shared Docker network (`fluxer-shared`).
|
|
Caddy acts as the internal reverse proxy, routing all traffic on port `8088` to the appropriate service.
|
|
Cloudflared tunnels external HTTPS traffic (no open inbound ports required).
|
|
|
|
```
|
|
Internet
|
|
└── Cloudflare Tunnel (cloudflared)
|
|
└── Caddy :8088
|
|
├── /api/* → api:8080 (Node.js REST API)
|
|
├── /gateway/* → gateway:8080 (Erlang WebSocket gateway)
|
|
├── /media/* → media:8080 (media proxy)
|
|
├── /admin/* → admin:8080 (Elixir admin panel)
|
|
├── /marketing/* → marketing:8080 (Elixir marketing site)
|
|
├── /metrics/* → metrics:8080 (Rust metrics service)
|
|
├── /livekit/* → livekit:7880 (LiveKit voice/video)
|
|
├── /s3/* → minio:9000 (S3-compatible object storage)
|
|
└── /* → /app/dist (built frontend SPA)
|
|
```
|
|
|
|
### Services
|
|
|
|
| Container | Image/Build | Purpose |
|
|
|-----------|-------------|---------|
|
|
| `caddy` | `caddy:2` | Internal reverse proxy |
|
|
| `cloudflared` | `cloudflare/cloudflared` | Tunnel (no inbound ports) |
|
|
| `api` | `node:24-bookworm-slim` | REST API (`tsx watch`) |
|
|
| `worker` | `node:24-bookworm-slim` | Background job worker |
|
|
| `gateway` | `erlang:28-slim` | WebSocket gateway (compiled with rebar3) |
|
|
| `media` | build from `fluxer_media_proxy/` | Media proxy |
|
|
| `admin` | build from `fluxer_admin/` | Admin panel |
|
|
| `marketing` | build from `fluxer_marketing/` | Marketing site |
|
|
| `docs` | `node:24-bookworm-slim` | Docs site |
|
|
| `metrics` | build from `fluxer_metrics/` | Metrics aggregator |
|
|
| `postgres` | `postgres:17` | Primary database |
|
|
| `cassandra` | `scylladb/scylla:latest` | Time-series / message data |
|
|
| `redis` | `valkey/valkey:latest` | Cache / pub-sub |
|
|
| `minio` | `minio/minio` | S3-compatible object storage |
|
|
| `meilisearch` | `getmeili/meilisearch:v1.25.0` | Full-text search |
|
|
| `clamav` | `clamav/clamav:latest` | Virus scanning (can be disabled) |
|
|
| `livekit` | `livekit/livekit-server:latest` | Voice & video |
|
|
| `cassandra-migrate` | `debian:bookworm-slim` | One-shot DB migration runner |
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
- Debian/Ubuntu VM (tested on Debian 12 bookworm)
|
|
- Docker + Docker Compose plugin installed
|
|
- `git` installed
|
|
- A Cloudflare tunnel token (from Cloudflare Zero Trust dashboard)
|
|
- Domain pointed at Cloudflare (e.g. `st.vish.gg`)
|
|
- At least 4 vCPU / 8GB RAM recommended (ScyllaDB + ClamAV are hungry)
|
|
|
|
### Install Docker (if not present)
|
|
|
|
```bash
|
|
curl -fsSL https://get.docker.com | sh
|
|
systemctl enable --now docker
|
|
```
|
|
|
|
---
|
|
|
|
## Step-by-step rebuild
|
|
|
|
### 1. Clone the repo
|
|
|
|
```bash
|
|
git clone https://github.com/fluxerapp/fluxer /root/fluxer
|
|
cd /root/fluxer
|
|
```
|
|
|
|
### 2. Create the shared Docker network
|
|
|
|
Must be created before starting any containers — it's declared `external: true` in the compose file.
|
|
|
|
```bash
|
|
docker network create fluxer-shared
|
|
```
|
|
|
|
### 3. Build the frontend app
|
|
|
|
Caddy serves the pre-built SPA from `fluxer_app/dist/`. This must be built before starting Caddy,
|
|
otherwise it will mount an empty directory and serve nothing.
|
|
|
|
```bash
|
|
# Install pnpm
|
|
corepack enable pnpm
|
|
|
|
# Install workspace deps
|
|
pnpm install
|
|
|
|
# Build the app (outputs to fluxer_app/dist/)
|
|
cd fluxer_app
|
|
pnpm build
|
|
cd ..
|
|
```
|
|
|
|
> Note: The build requires Node 22+ and pnpm. If the VM doesn't have Node installed globally,
|
|
> run the build inside a container:
|
|
> ```bash
|
|
> docker run --rm -v $(pwd):/workspace -w /workspace/fluxer_app node:24-bookworm-slim \
|
|
> bash -lc "corepack enable pnpm && CI=true pnpm install && pnpm build"
|
|
> ```
|
|
|
|
### 4. Build the cassandra-migrate binary
|
|
|
|
The Cassandra migration runner is a Rust binary that must be pre-compiled.
|
|
|
|
```bash
|
|
# Install Rust if needed
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
source ~/.cargo/env
|
|
|
|
# Build
|
|
cd scripts/cassandra-migrate
|
|
cargo build --release
|
|
cd ../..
|
|
```
|
|
|
|
The compose file expects the binary at `scripts/cassandra-migrate/target/release/cassandra-migrate`.
|
|
|
|
### 5. Set up the environment file
|
|
|
|
```bash
|
|
cp dev/.env.example dev/.env
|
|
```
|
|
|
|
Edit `dev/.env` and fill in:
|
|
|
|
| Variable | What to set |
|
|
|----------|-------------|
|
|
| `FLUXER_APP_ENDPOINT` | Your public URL e.g. `https://st.vish.gg` |
|
|
| `FLUXER_API_PUBLIC_ENDPOINT` | `https://st.vish.gg/api` |
|
|
| `FLUXER_GATEWAY_ENDPOINT` | `wss://st.vish.gg/gateway` |
|
|
| `FLUXER_MARKETING_ENDPOINT` | `https://st.vish.gg` |
|
|
| `FLUXER_ADMIN_ENDPOINT` | `https://st.vish.gg/admin` |
|
|
| `FLUXER_MEDIA_ENDPOINT` | `https://st.vish.gg/media` |
|
|
| `FLUXER_INVITE_ENDPOINT` | `https://st.vish.gg` |
|
|
| `CLOUDFLARE_TUNNEL_TOKEN` | Token from Cloudflare Zero Trust dashboard |
|
|
| `SECRET_KEY_BASE` | Generate: `openssl rand -hex 64` |
|
|
| `GATEWAY_RPC_SECRET` | Generate: `openssl rand -hex 32` |
|
|
| `GATEWAY_ADMIN_SECRET` | Generate: `openssl rand -hex 32` |
|
|
| `MEDIA_PROXY_SECRET_KEY` | Generate: `openssl rand -hex 32` |
|
|
| `SUDO_MODE_SECRET` | Generate: `openssl rand -hex 32` |
|
|
| `LIVEKIT_API_KEY` | Generate: `openssl rand -hex 16` |
|
|
| `LIVEKIT_API_SECRET` | Generate: `openssl rand -hex 32` |
|
|
| `VAPID_PUBLIC_KEY` | Generate with web-push: `npx web-push generate-vapid-keys` |
|
|
| `VAPID_PRIVATE_KEY` | (paired with above) |
|
|
| `ADMIN_OAUTH2_CLIENT_ID` | OAuth2 app ID (from Fluxer admin panel after first boot) |
|
|
| `ADMIN_OAUTH2_CLIENT_SECRET` | OAuth2 secret |
|
|
| `PASSKEY_RP_ID` | Your domain e.g. `st.vish.gg` |
|
|
| `PASSKEY_ALLOWED_ORIGINS` | `https://st.vish.gg` |
|
|
|
|
Everything else can stay at its default dev value on first boot.
|
|
|
|
### 6. Set up the LiveKit config
|
|
|
|
```bash
|
|
cp dev/livekit.yaml dev/livekit.yaml
|
|
```
|
|
|
|
Edit `dev/livekit.yaml` — replace the API key/secret with the values you set in `.env`:
|
|
|
|
```yaml
|
|
keys:
|
|
"<LIVEKIT_API_KEY>": "<LIVEKIT_API_SECRET>"
|
|
|
|
webhook:
|
|
api_key: "<LIVEKIT_API_KEY>"
|
|
urls:
|
|
- "http://api:8080/webhooks/livekit"
|
|
```
|
|
|
|
### 7. Start the stack
|
|
|
|
```bash
|
|
cd /root/fluxer/dev
|
|
docker compose up -d
|
|
```
|
|
|
|
On first boot, `cassandra-migrate` will run automatically and apply schema migrations.
|
|
ScyllaDB takes ~90 seconds to become healthy before migrations run.
|
|
|
|
Watch progress:
|
|
```bash
|
|
docker compose logs -f cassandra-migrate
|
|
docker compose logs -f api
|
|
```
|
|
|
|
### 8. Verify everything is up
|
|
|
|
```bash
|
|
docker compose ps
|
|
```
|
|
|
|
Expected running containers: `caddy`, `cloudflared`, `api`, `worker`, `gateway`, `media`, `admin`,
|
|
`marketing`, `docs`, `metrics`, `clamav`, `meilisearch`, `redis`, `minio`, `livekit`
|
|
|
|
Expected exited (one-shot): `cassandra-migrate`, `minio-setup`
|
|
|
|
The app should be accessible at your configured domain via the Cloudflare tunnel.
|
|
|
|
---
|
|
|
|
## Managing the stack
|
|
|
|
### Start / stop
|
|
|
|
```bash
|
|
cd /root/fluxer/dev
|
|
|
|
# Start all
|
|
docker compose up -d
|
|
|
|
# Stop all (keep volumes)
|
|
docker compose down
|
|
|
|
# Stop and wipe all data
|
|
docker compose down -v
|
|
```
|
|
|
|
### View logs
|
|
|
|
```bash
|
|
# All services
|
|
docker compose logs -f
|
|
|
|
# Specific service
|
|
docker compose logs -f api
|
|
docker compose logs -f gateway
|
|
```
|
|
|
|
### Rebuild a specific service after code changes
|
|
|
|
```bash
|
|
# Rebuild and restart one service
|
|
docker compose up -d --build media
|
|
|
|
# Rebuild frontend app and restart caddy
|
|
cd /root/fluxer
|
|
pnpm --filter fluxer_app build
|
|
docker compose -f dev/compose.yaml restart caddy
|
|
```
|
|
|
|
### Update from upstream
|
|
|
|
```bash
|
|
cd /root/fluxer
|
|
git pull origin main
|
|
|
|
# Rebuild frontend
|
|
pnpm install
|
|
pnpm --filter fluxer_app build
|
|
|
|
# Restart services that auto-install deps (api, worker, docs)
|
|
docker compose -f dev/compose.yaml restart api worker docs
|
|
|
|
# Rebuild services with Dockerfiles
|
|
docker compose -f dev/compose.yaml up -d --build media admin marketing metrics
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration reference
|
|
|
|
### Key files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `dev/compose.yaml` | Main docker compose for the dev stack |
|
|
| `dev/.env` | All environment variables (not committed — see `.env.example`) |
|
|
| `dev/Caddyfile.dev` | Caddy reverse proxy routing config |
|
|
| `dev/livekit.yaml` | LiveKit server config (API keys, webhook URL) |
|
|
|
|
### Port mappings (host)
|
|
|
|
| Port | Service |
|
|
|------|---------|
|
|
| `8088` | Caddy (all traffic enters here, then routed internally) |
|
|
| `7880` | LiveKit HTTP |
|
|
| `7882/udp` | LiveKit RTC |
|
|
| `7999/udp` | LiveKit RTC |
|
|
| `9042` | ScyllaDB CQL (direct access) |
|
|
| `8123` | ClickHouse (only with `--profile clickhouse`) |
|
|
|
|
All other services communicate internally over `fluxer-shared` Docker network.
|
|
|
|
### Optional profiles
|
|
|
|
```bash
|
|
# Enable ClickHouse-backed metrics
|
|
docker compose --profile clickhouse up -d
|
|
|
|
# Enable search (meilisearch is included by default in dev)
|
|
docker compose --profile search up -d
|
|
```
|
|
|
|
---
|
|
|
|
## Cloudflare tunnel setup
|
|
|
|
1. Go to Cloudflare Zero Trust → Networks → Tunnels → Create tunnel
|
|
2. Name it (e.g. `fluxer-seattle`)
|
|
3. Copy the tunnel token → set as `CLOUDFLARE_TUNNEL_TOKEN` in `dev/.env`
|
|
4. Add a public hostname:
|
|
- Subdomain: `st` (or your chosen subdomain)
|
|
- Domain: `vish.gg`
|
|
- Service: `http://localhost:8088`
|
|
5. The `cloudflared` container will connect automatically on `docker compose up`
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Gateway won't start
|
|
|
|
The Erlang gateway compiles from source on first start (`rebar3 compile`) — this takes 2-5 minutes.
|
|
Watch with `docker compose logs -f gateway`. If it exits, check for missing deps or compilation errors.
|
|
|
|
### ScyllaDB/Cassandra migration fails
|
|
|
|
ScyllaDB needs ~90 seconds to fully initialise. The `cassandra-migrate` container has a `sleep 30`
|
|
but on slow machines it may still fail. Restart it manually:
|
|
|
|
```bash
|
|
docker compose run --rm cassandra-migrate
|
|
```
|
|
|
|
### API exits immediately
|
|
|
|
Almost always a missing or malformed `.env` value. Check:
|
|
|
|
```bash
|
|
docker compose logs api | head -30
|
|
```
|
|
|
|
Common causes: `DATABASE_URL` unreachable, missing `SECRET_KEY_BASE`, malformed `REDIS_URL`.
|
|
|
|
### Frontend shows blank page
|
|
|
|
The `fluxer_app/dist/` directory is empty — the app hasn't been built yet.
|
|
Run the build step (Step 3 above) then restart Caddy:
|
|
|
|
```bash
|
|
docker compose restart caddy
|
|
```
|
|
|
|
### ClamAV takes forever to start
|
|
|
|
ClamAV downloads its virus definition database on first start (~500MB). This is normal.
|
|
It won't be marked healthy until the download completes (~5-10 min on first boot).
|
|
ClamAV can be disabled by setting `CLAMAV_ENABLED=false` in `.env` — the other services don't depend on it.
|
|
|
|
---
|
|
|
|
## Seattle reference deployment
|
|
|
|
The running instance at `st.vish.gg` uses this exact setup. Key details:
|
|
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| Host | Seattle VM (`66.94.122.170` / Tailscale `100.82.197.124`) |
|
|
| SSH | `ssh root@seattle` or `ssh root@seattle-tailscale` |
|
|
| Install path | `/root/fluxer/` |
|
|
| Compose path | `/root/fluxer/dev/` |
|
|
| Domain | `st.vish.gg` |
|
|
| Tunnel | Cloudflare Zero Trust (`cloudflared`) |
|
|
| `AUTO_JOIN_INVITE_CODE` | `FRIENDS` (anyone with this code can register) |
|
|
|
|
To check status on seattle:
|
|
|
|
```bash
|
|
ssh seattle-tailscale "cd /root/fluxer/dev && docker compose ps"
|
|
ssh seattle-tailscale "cd /root/fluxer/dev && docker compose logs --tail 20 api"
|
|
```
|