LevelChat self-host runs the same images as our managed cloud. The only difference is the license you point the platform at — your plan's entitlements gate per-room caps, recording, AI features, and other premium capabilities. There is no "Community Edition vs. Enterprise Edition" feature drift.
How self-host works
1. Buy license → levelchat.io/self-host (Stripe checkout)
2. Receive email → contains your license JWT + a download URL
3. Run installer → one docker compose command
4. Done → your workspace at https://<your-domain>/consoleTotal time from purchase to a working room: ~5 minutes.
Prereqs
- A Linux box with Docker Engine 24+ and
docker composev2 (Hetzner CPX21 / DigitalOcean Premium 4G / EC2 t3.large all work). - A public DNS A-record pointing at the box (e.g.
app.acme.com → 167.235.x.x). - An SMTP relay for transactional emails — SendGrid, Mailgun, AWS SES, or any provider speaking STARTTLS + AUTH PLAIN on port 587. Required for email verification, member invitations, and password reset.
- Outbound TCP 443 to
license.levelchat.iofor daily license heartbeats (~1 KB/day).
Install
# 1. Download the installer (link comes in your purchase email)
curl -fsSL "$LEVELCHAT_DOWNLOAD_URL" | tar -xz
cd levelchat-self-host
# 2. Configure
cp .env.example .env
$EDITOR .env
# Required values:
# LC_PUBLIC_DOMAIN=app.acme.com
# LC_LETSENCRYPT_EMAIL=ops@acme.com
# LICENSE_JWT=<paste from your purchase email>
# SMTP_HOST=smtp.mailgun.org
# SMTP_USERNAME=postmaster@mg.acme.com
# SMTP_PASSWORD=<your relay secret>
# SMTP_FROM=noreply@acme.com
# POSTGRES_PASSWORD=<generate a strong password>
# 3. Bring everything up
docker compose up -d
# 4. Verify
docker compose ps # all containers should report 'Up (healthy)'
curl https://api.app.acme.com/healthz
curl https://app.acme.com/console/loginWhat just got deployed
| Component | Container | Purpose |
|---|---|---|
| Studio (admin frontend) | studio | Customer-facing management console |
| Landing | landing | Your app.acme.com marketing surface |
| Docs | docs | Developer documentation site |
| Auth | auth | Sign in, sign up, sessions, email verification |
| Admin API | admin-api | Studio's read/write backend |
| Signaling | signaling | WebSocket signaling for the SDK |
| Media SFU | media-sfu | mediasoup SFU — bytes flow through this |
| Recording | recording | Composes and stores recordings |
| License | license | Verifies your license JWT, runs heartbeat |
| Postgres | postgres | All persistent state |
| Redis | redis | Sessions, rate limits, ephemeral state |
| NATS | nats | Internal pub/sub between services |
| MinIO | minio | S3-compatible recording storage |
| Traefik | traefik | TLS termination via Let's Encrypt |
Total: ~14 containers. RAM at idle: ~2.5 GB. CPU floor: ~5 % of one core.
First steps after install
- Open the Studio:
https://app.<your-domain>/console/signup - Sign up with your email — you'll receive a verification link from your SMTP relay.
- Verify → land on the dashboard → create your first project.
- Generate an API key under Settings → Developer and start integrating the SDK.
License heartbeat
license-svc phones home to https://license.levelchat.io/v1/heartbeat once a day with:
- Your license JWT (so we can confirm it's still active)
- An instance fingerprint (machine-id + hostname) — used to detect license sharing across unrelated clusters
- Aggregate quota counters (rooms-created, participant-minutes, recording-GB) — used for billing on usage-priced plans
If the heartbeat is blocked for 7 consecutive days the instance enters a degraded
mode (existing rooms keep working, new rooms refuse). 30 days → frozen (read-only).
Heartbeats are small enough (~1 KB) that even a quarterly air-gapped export+import is
feasible — see LICENSE_HEARTBEAT_OFFLINE=true in .env.example.
What stays in your perimeter
- All recording bytes. Recordings live in your local MinIO (or your own S3 bucket if
you set
S3_ENDPOINT=...in.env). LevelChat never sees them. - All chat messages. Persisted in your Postgres.
- All identities + rooms + members metadata. Entirely on your DB.
- All viewer fan-out for ≤2k-viewer broadcasts. The cascade SFU edge for >10k viewers is the only path that calls back to our cloud — and it's opt-in.
The only data that leaves your perimeter is the daily license heartbeat (license JWT + fingerprint + counters). No PII, no media bytes.
Cloud-augmented features (optional)
A handful of premium features call back to our cloud — they're opt-in and clearly marked in Studio → Settings:
- AI transcription (
captions.levelchat.io— audio streamed for transcription) - AI summary / agent (
agents.levelchat.io) - Cascade SFU for ≥10k-viewer broadcasts (your origin SFU pushes to our edge)
- AV1 transcode-on-egress (raw recording uploaded to our transcoder, returned compressed)
Self-host installs come with all four off by default. Enabling them counts against your plan's per-meter quotas.
Operating the install
Backup
docker exec lc-postgres pg_dump -U levelchat levelchat | gzip > "/backups/pg-$(date +%F).sql.gz"
docker run --rm --network lc-net minio/mc \
alias set lc http://minio:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
docker run --rm --network lc-net -v /backups:/out minio/mc \
mirror lc/lc-recordings /out/recordings/restic against /var/lib/docker/volumes/ is the no-thinking equivalent.
Upgrades
docker compose pull # pulls newest tagged versions for the same major
docker compose up -d
# DB migrations run automatically on auth + admin-api boot.Pin a version for change-control:
LC_VERSION=1.4.2 docker compose up -dRollback
LC_VERSION=1.4.1 docker compose up -d --force-recreateDB migrations are intentionally additive — rolling back the binary against a
forward-migrated DB works for the previous minor version. For multi-version rollbacks,
restore Postgres from a pg_dump first.
Logs
docker compose logs -f auth admin-api signaling # filter by service
docker compose logs --since 1h | grep ERROR # quick triageFor production observability, the docker-compose.self-host-ha.yml variant ships a
Grafana + Loki bundle pre-wired. The small mode skips it to keep RAM low.
Three deployment sizes
| Mode | Best for | Cost ballpark | TLS | HA |
|---|---|---|---|---|
docker-compose.self-host-small.yml | Demos, internal tools, ≤200 concurrent | $20–40/mo | Traefik + Let's Encrypt | no |
docker-compose.self-host-ha.yml | Production self-host, ≤2k concurrent | $90–200/mo | Traefik + Let's Encrypt | postgres replica + redis sentinel |
infra/kubernetes/helm/levelchat | ≥2k concurrent or multi-region | varies | cert-manager | bring your own |
Pick self-host-small.yml for the first install — you can migrate to the HA compose or
Helm chart later without changing the application configuration.
Need help?
- Email: self-host@levelchat.io
- Status page: status.levelchat.io — incidents on the license + downloads endpoints (the only services that affect your install)
- Runbook: the installer ships a
RUNBOOK.mdcovering the 12 most common operational scenarios (full disk, missed heartbeat, DB migration stuck, …)