Self-Hosting
Run your own Hopp server. Includes PostgreSQL, Redis, LiveKit (WebRTC), and Caddy (auto-TLS).
Prerequisites
Section titled “Prerequisites”- A server with Docker and Docker Compose installed
- A public hostname for your server. Either:
- A real domain with A records for
<DOMAIN>andlivekit.<DOMAIN>(or wildcard*.<DOMAIN>) pointing to your server’s IP. - Or
sslip.iofor quick testing — use<your-ip>.sslip.ioasDOMAIN(e.g.51.15.42.10.sslip.io). Resolves automatically, no DNS setup. Subdomains work (livekit.<your-ip>.sslip.io). Real domain recommended for production.
- A real domain with A records for
- Open firewall ports (see Firewall below)
Quickstart (6 steps)
Section titled “Quickstart (6 steps)”1. Clone the repo
Section titled “1. Clone the repo”git clone --depth 1 https://github.com/gethopp/hopp.gitcd hopp/selfhost2. Configure environment
Section titled “2. Configure environment”cp .env.example .envEdit .env and set at minimum:
DOMAIN=hopp.example.comSESSION_SECRET=<openssl rand -base64 32>LIVEKIT_API_KEY=<openssl rand -base64 32>LIVEKIT_API_SECRET=<openssl rand -base64 64>POSTGRES_PASSWORD=<openssl rand -base64 24>Generate secrets with the openssl commands shown.
3. Start the stack
Section titled “3. Start the stack”docker compose up -d4. Verify
Section titled “4. Verify”curl https://hopp.example.com/api/health# → OK5. Create the first account
Section titled “5. Create the first account”Open https://hopp.example.com in a browser and sign up. The first registered account becomes the team admin owner.
Firewall
Section titled “Firewall”Many cloud providers (Scaleway DEV, Hetzner Cloud, basic DigitalOcean droplets) do not apply a firewall by default — these ports will already be reachable. Skip this section unless your provider has a security group, network ACL, or you’ve enabled ufw/firewalld on the host.
AWS, GCP, Azure, and Scaleway PRO/PROD instances typically need explicit security-group rules.
Open these ports if your provider applies a firewall:
| Port | Protocol | Purpose |
|---|---|---|
| 80 | TCP | HTTP (TLS challenge) |
| 443 | TCP | HTTPS |
| 7881 | TCP | LiveKit TCP fallback |
| 50000-60000 | UDP | WebRTC media |
Caddy handles TLS termination on ports 80/443. LiveKit media bypasses Caddy and goes directly to the server.
Web app and desktop
Section titled “Web app and desktop”The web app served by your backend works without a rebuild — it derives the API URL from window.location at runtime.
For the desktop app, you can set a custom backend URL at runtime via Settings > Custom Backend URL (see Settings docs).
Advanced
Section titled “Advanced”Custom ports
Section titled “Custom ports”Copy compose.override.example.yml to compose.override.yml and adjust port mappings.
Behind your own reverse proxy
Section titled “Behind your own reverse proxy”If you already run nginx/traefik/caddy, skip the bundled Caddy:
- Remove the
caddyservice fromcompose.yml(or use an override) - Point your proxy to
backend:1926(Docker DNS) and127.0.0.1:7880(LiveKit on host networking) - Set
USE_TLS=false(already the default) - Set
LIVEKIT_SERVER_URL=wss://livekit.yourdomain.comin.env
Local development (running selfhost stack on your machine)
Section titled “Local development (running selfhost stack on your machine)”To run the self-hosted stack against localhost instead of a public domain:
1. Generate localhost TLS certs
Section titled “1. Generate localhost TLS certs”The backend serves TLS directly when USE_TLS=true. Generate certs from the backend/ folder:
cd ../backendtask create-certs # uses mkcert; installs the local CA on first runThis produces backend/certs/localhost.pem and backend/certs/localhost-key.pem, which the override mounts into the backend container.
2. Use the bundled local override
Section titled “2. Use the bundled local override”A ready-made compose.override.yml is committed for local use. It:
- exposes
backend:1926directly on the host (skipping Caddy) - enables
USE_TLS=trueand mounts../backend/certs - swaps LiveKit to
livekit.dev.yaml(narrow UDP range50000-50100, avoids macOS port-conflict storms)
Set .env:
DOMAIN=localhostLIVEKIT_SERVER_URL=ws://localhost:7880USE_TLS=trueThen:
docker compose up -d db cache livekit backend# (skip caddy — not needed for localhost)Backend reachable at https://localhost:1926.