6.1 KiB
Quick Start: Docker
This guide gets Chat Grid running with Docker Compose and optionally behind a Caddy reverse proxy for HTTPS.
Architecture
Browser → Caddy (TLS) → nginx:80 → static files
└→ /ws → Python server:4474
→ LiveKit server:7880 (signaling WebSocket)
→ LiveKit server:7881/tcp, 7882/udp (media transport)
nginx handles the built client files and the WebSocket proxy to the Python server. Caddy (or any reverse proxy) sits in front for TLS termination.
Voice audio flows through a LiveKit SFU (Selective Forwarding Unit) rather than direct peer-to-peer connections. The browser connects to LiveKit directly for audio, and to the Python server (via nginx) for everything else (chat, position, items, auth).
1. Prerequisites
- Docker with Compose plugin (
docker compose version) - Git
2. Clone and Configure
git clone <repo-url> chat_grid
cd chat_grid
cp .env.example .env
Edit .env:
# Generate a secret: openssl rand -hex 32
CHGRID_AUTH_SECRET=your-long-random-secret-here
# The URL your browser uses to reach the app (no trailing slash)
# Use http://localhost for local-only access
CHGRID_HOST_ORIGIN=https://your-domain.com
# LiveKit SFU settings — keys must match livekit.yaml
LIVEKIT_API_KEY=devkey
LIVEKIT_API_SECRET=a]3jK$2pL9mN#vQ8wR5tY7uI0oP4sD6fG
# LiveKit URL as seen by the browser (not Docker-internal).
# Local: ws://localhost:7880
# Production behind Caddy: wss://livekit.your-domain.com
LIVEKIT_URL=ws://localhost:7880
LiveKit credentials
The default livekit.yaml ships with a dev key pair (devkey / a]3jK$2pL9mN#vQ8wR5tY7uI0oP4sD6fG). For production, generate your own:
# Pick any key name and a long random secret
echo "mykey: $(openssl rand -base64 32)"
Put the key/secret in both livekit.yaml (under keys:) and .env (LIVEKIT_API_KEY / LIVEKIT_API_SECRET).
3. Build and Start
docker compose up --build -d
This builds the server and client images, pulls the LiveKit image, and starts all three containers:
| Container | Purpose | Ports |
|---|---|---|
client (nginx) |
Static files + WebSocket proxy | 127.0.0.1:4474 → 80 |
server (Python) |
Game logic, auth, token generation | Internal only |
livekit |
Audio SFU | 7880, 7881/tcp, 7882/udp |
4. Bootstrap the First Admin
This only needs to be run once on first startup:
docker compose exec server python main.py --bootstrap-admin
Follow the prompts to create the initial admin account.
5. Open the App
- Local: http://localhost:4474
- With Caddy: https://your-domain.com (after step 6)
Log in with the admin credentials you just created.
6. Production: Caddy Reverse Proxy (HTTPS)
For production you need Caddy (or another reverse proxy) in front of both nginx and LiveKit. You need two domains (or a domain + subdomain):
- One for the app (e.g.
your-domain.com) - One for LiveKit (e.g.
livekit.your-domain.com)
Caddyfile
your-domain.com {
reverse_proxy localhost:4474
}
livekit.your-domain.com {
reverse_proxy localhost:7880
}
Caddy automatically:
- Provisions TLS certificates via Let's Encrypt
- Forwards WebSocket upgrade headers (no extra config needed)
- Passes app traffic (static files +
/ws) to nginx on port 4474 - Passes LiveKit signaling to port 7880
After editing your Caddyfile:
caddy reload --config /etc/caddy/Caddyfile
Update .env for production
CHGRID_HOST_ORIGIN=https://your-domain.com
LIVEKIT_URL=wss://livekit.your-domain.com
Then restart the server to pick up the new LiveKit URL:
docker compose up -d server
Firewall / port forwarding
The following ports must be reachable from the internet:
| Port | Protocol | Purpose |
|---|---|---|
| 443 | TCP | Caddy HTTPS (app + LiveKit signaling) |
| 7881 | TCP | LiveKit media (TCP fallback) |
| 7882 | UDP | LiveKit media (primary, WebRTC) |
If you are not using Caddy in front of LiveKit (i.e. clients connect to LiveKit directly without TLS), also open port 7880 (TCP).
Note: The old peer-to-peer WebRTC setup required users to have a direct network path to each other. With LiveKit, all media goes through the server, so users behind NAT connect without issue as long as they can reach the ports above.
7. Persistence
User data (SQLite DB, items.json) is stored in a named Docker volume called server-runtime. It survives container restarts and rebuilds:
docker compose restart # data is preserved
docker compose up --build -d # rebuild images, data is preserved
To inspect or back up the volume:
docker volume inspect chat_grid_server-runtime
Common Commands
# View logs
docker compose logs -f
# View server logs only
docker compose logs -f server
# View LiveKit logs only
docker compose logs -f livekit
# Stop everything
docker compose down
# Stop and delete the data volume (destructive)
docker compose down -v
# Rebuild after code changes
docker compose up --build -d
Troubleshooting
WebSocket won't connect / "Disconnected" status
- Check that
CHGRID_HOST_ORIGINin.envexactly matches the URL in your browser (includinghttp/httpsand no trailing slash). - Check server logs:
docker compose logs server
No audio / "LiveKit disconnected"
- Verify
LIVEKIT_URLin.envis reachable from the browser (not a Docker-internal hostname likelivekit:7880). - Check LiveKit logs:
docker compose logs livekit - For production, make sure ports 7881/tcp and 7882/udp are open and forwarded.
- If behind Caddy, make sure the LiveKit subdomain is configured and
LIVEKIT_URLuseswss://.
LiveKit secret mismatch
- The key/secret in
.env(LIVEKIT_API_KEY/LIVEKIT_API_SECRET) must exactly match what is inlivekit.yamlunderkeys:.
Changing the host port
- The client binds to
127.0.0.1:4474by default. To use a different port, change"127.0.0.1:4474:80"indocker-compose.ymland update your Caddyreverse_proxytarget to match.
Admin bootstrap fails
- Make sure the server container is healthy first:
docker compose ps