Files
chat_grid/quickstart.md

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

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_ORIGIN in .env exactly matches the URL in your browser (including http/https and no trailing slash).
  • Check server logs: docker compose logs server

No audio / "LiveKit disconnected"

  • Verify LIVEKIT_URL in .env is reachable from the browser (not a Docker-internal hostname like livekit: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_URL uses wss://.

LiveKit secret mismatch

  • The key/secret in .env (LIVEKIT_API_KEY/LIVEKIT_API_SECRET) must exactly match what is in livekit.yaml under keys:.

Changing the host port

  • The client binds to 127.0.0.1:4474 by default. To use a different port, change "127.0.0.1:4474:80" in docker-compose.yml and update your Caddy reverse_proxy target to match.

Admin bootstrap fails

  • Make sure the server container is healthy first: docker compose ps