Skip to content

Troubleshooting

Common issues and how to diagnose them.

The backend reports unhealthy. Check what’s failing:

Terminal window
docker compose ps
docker compose logs backend | tail -50
docker compose exec backend wget -qO- http://localhost:8095/readyz

/readyz probes Postgres + Redis + LiveKit. The JSON response lists which dependency is down. Usually:

  • Postgres not ready yet (wait 10–20s after docker compose up)
  • Redis password mismatch between .env and the container
  • LiveKit signaling unreachable (check LIVEKIT_HOST env var resolves)

LiveKit connection refused / WebSocket fails

Section titled “LiveKit connection refused / WebSocket fails”
Terminal window
docker compose logs livekit | tail -30

Two common causes:

  1. API key/secret mismatch. infra/livekit.yaml has keys: { <KEY>: <SECRET> } and the backend reads LIVEKIT_API_KEY / LIVEKIT_API_SECRET from .env. They must match exactly. Regenerate both sides if unsure.
  2. UDP ports blocked. WebRTC media flows over UDP 50000–50200 (configurable in infra/livekit.yaml). On a firewalled host these must be open. TCP fallback uses 7881 — slower but works through restrictive networks.

Symptom: login API returns 200 OK but the browser immediately bounces back to /login. The session cookie wasn’t accepted.

Causes:

  • DOMAIN env var doesn’t match the host you’re hitting (cookie domain mismatch)
  • You’re accessing via HTTP, not HTTPS — Secure cookies require TLS in modern browsers
  • You’re behind a proxy that strips Set-Cookie — check Caddy logs
Terminal window
docker compose logs caddy | grep -i cookie

Access tokens have a 15-minute TTL. On expiry the studio auto-refreshes silently on the next request via the opaque refresh token (30 days sliding). If you see 401s in burst:

  • Refresh token also expired → user must re-login
  • Clock skew between client and server > 30s → fix NTP on the host
  • Refresh token revoked (logged out elsewhere) → re-login
Terminal window
docker compose exec backend goose -dir /app/migrations \
postgres "$DATABASE_URL" status

If goose is out of sync (a migration was applied manually, or a previous run crashed mid-migration), you can:

Terminal window
# See the state
goose -dir migrations postgres "$DATABASE_URL" status
# Force to a known version (CAREFUL — does not run the SQL)
goose -dir migrations postgres "$DATABASE_URL" version
goose -dir migrations postgres "$DATABASE_URL" up-by-one

For a fully botched dev DB, drop and recreate:

Terminal window
docker compose down postgres
docker volume rm commentary_postgres-data
docker compose up -d postgres backend

Commentator kiosk shows “no permission” for microphone

Section titled “Commentator kiosk shows “no permission” for microphone”

Browser blocked mic access. The kiosk is Chrome-only (uses AudioContext.sinkId for output device routing). In Chrome:

  1. Click the lock icon in the address bar
  2. Site settings → Microphone → Allow
  3. Reload the page

On macOS / Windows also check OS-level mic permission for Chrome.

Terminal window
docker compose logs caddy | grep -i acme

Typical issues:

  • Port 80 blocked or already used by another service (Caddy needs :80 for HTTP-01)
  • DNS hasn’t propagated yet — wait 5 minutes then docker compose restart caddy
  • Rate-limit hit on Let’s Encrypt (5 certs per domain per week) — wait or use the staging endpoint via acme_ca https://acme-staging-v02.api.letsencrypt.org/directory in the Caddyfile temporarily

Open an issue with the output of:

Terminal window
docker compose ps
docker compose logs --tail=100 backend caddy livekit

Scrub any secrets before posting.