SpacetimeDB
Real-time chat/game state backend. Required for channels, messages, membership, and sync.
Beginner-first production tutorial (Ubuntu + Docker Compose)
LetsChat is not a single server. It is a small backend stack: chat state, auth, voice signaling, file storage, and startup discovery. This guide explains what each part does, why it matters, and then walks you through one of two deployment paths.
SpacetimeDB
Real-time chat/game state backend. Required for channels, messages, membership, and sync.
Auth Service
Login/session API and token minting. Required for user authentication and LiveKit token creation.
LiveKit
Voice/media signaling and transport. Required for voice channels and real-time audio.
MinIO
S3-compatible object storage. Required for uploads, attachments, and download URLs.
Module Init
One-shot container that publishes the SpacetimeDB WASM module on startup. Runs automatically and exits cleanly.
Discovery Endpoint (/.well-known/letschat.json)
Lets app startup auto-discover your backend from one URL (for example https://connect.example.com).
Without this, users must paste a full join link manually.
Before applying setup or upgrade steps, review Breaking Changes to avoid version mismatch and migration issues.
Both paths deploy the same backend stack. The difference is how HTTPS traffic reaches your server.
Path A: Cloudflare Tunnel
What: Routes auth/chat/files/connect over Cloudflare Tunnel; LiveKit media remains direct via forwarded ports.
Why: Keeps web services off direct public IP while still supporting voice traffic.
Best for: Home servers where you want Cloudflare ingress control without exposing 80/443 directly.
Read the guide →
Path B: Caddy Reverse Proxy
What: Caddy terminates TLS on your host and proxies auth/chat/files/lk/connect locally.
Why: Simple standard architecture with fewer external moving parts.
Best for: Operators comfortable exposing 80/443 and managing direct DNS to host IP.
Read the guide →
| Problem | Likely cause | Fix |
|---|---|---|
| Voice joins but no audio | LiveKit media ports not forwarded | Forward 44381/tcp+udp and 44382/udp to host |
| Setup discovery fails | connect.<domain> not routed to discovery container | Tunnel: connect → discovery:80; Caddy: set CONNECT_DOMAIN correctly |
| Upload/download errors | Wrong MINIO_PUBLIC_ENDPOINT | Set to public https://files... endpoint clients can reach |
| Caddy cert issuance fails | Cloudflare proxy enabled or closed 80/443 | Use DNS-only records and open 80/tcp, 443/tcp |
| LiveKit setup fails immediately | Wrong signaling scheme (ws:// vs wss://) | Tunnel often uses ws://lk...:44380; Caddy usually uses wss://lk... |
| Spacetime connect timeout | module-init failed or SpacetimeDB not yet healthy | Check docker logs letschat-module-init; it retries on failure automatically |