- Shell 100%
| .env.example | ||
| compose.yml | ||
| init-env.sh | ||
| livekit.yaml | ||
| README.md | ||
Chatto Docker Compose Example
This example deploys a clustered Chatto setup with:
- NATS - Message broker with JetStream persistence
- LiveKit - WebRTC media server for voice calls
- Chatto - App server connecting to external NATS
- Traefik - Reverse proxy (external, manages HTTPS and routing)
Prerequisites
- Docker and Docker Compose (v2) installed
- Traefik v3 running as a Docker container with a shared external network
- A domain pointing to your server (for automatic HTTPS)
- A
livekit.subdomain pointing to the same server (e.g.,livekit.chat.example.com) - A TLS certificate resolver configured in Traefik (e.g., Let's Encrypt)
Configuration
-
Generate
.envand the LiveKit config:./init-env.sh chat.example.com admin@example.comReplace
chat.example.comwith your Chatto domain andadmin@example.comwith the email address you will use for the first account.The script is the recommended setup path. It writes
.envandlivekit.generated.yaml, generates strong secrets, and keeps the shared values aligned:NATS_TOKENandCHATTO_NATS_CLIENT_TOKEN- Chatto cookie, core, and asset signing secrets
CHATTO_LIVEKIT_API_KEY/CHATTO_LIVEKIT_API_SECRET- The matching LiveKit
keys:and webhook URL
-
Edit
.envand review the generated values.In most cases, you should only need to change:
PUBLIC_URL- Your domain (e.g.,chat.example.com)CHATTO_OWNERS_EMAILS- Comma-separated verified email addresses that should become Chatto owners. Include the email address you will use for the first account.CHATTO_SMTP_*- Required for direct email/password registration, email verification, and password reset.PUIDandPGID- Optional host user/group IDs for files Chatto writes to mounted volumes. Defaults to1000:1000.CERTRESOLVER- Your Traefik certificate resolver name (e.g.,letsencrypt). Must match your Traefik configuration.TRAEFIK_NETWORK- The name of your external Traefik Docker network.
Leave
LIVEKIT_CONFIG_FILE=./livekit.generated.yamlunless you deliberately want to maintainlivekit.yamlby hand. -
Configure SMTP settings if you use direct email/password registration.
Manual setup fallback
Use this only if you cannot run init-env.sh or need to maintain secrets
outside this folder:
cp .env.example .env
Then edit .env, replace every placeholder secret with output from
openssl rand -hex 32, and edit livekit.yaml so the API key, API secret, and
webhook URL match .env. LiveKit requires the API secret to be at least 32
characters.
Usage
# Start the stack
docker compose up -d
# View logs
docker compose logs -f
# View logs for a specific service
docker compose logs -f chatto
# Restart a service
docker compose restart chatto
# Stop the stack
docker compose down
# Stop and remove volumes (deletes all data)
docker compose down -v
Scaling
# Scale to 5 replicas
docker compose up -d --scale chatto=5
Traefik automatically load-balances across all Chatto replicas.
Inspecting NATS
The Chatto image includes the nats CLI and writes a context for the runtime
NATS connection. Run it as the chatto user so the CLI reads the context from
/home/chatto:
docker compose exec -u chatto chatto nats stream ls
Updating
# Pull new images and recreate containers
docker compose pull
docker compose up -d
Volumes
Data is persisted in Docker volumes:
nats_data- NATS/JetStream data (messages, KV stores)caddy_data- TLS certificatescaddy_config- Caddy configuration cache
Disabling Voice Calls
If you don't need voice calls, remove the livekit service from compose.yml, delete the selected LiveKit config (livekit.generated.yaml or livekit.yaml), and remove the LiveKit environment variables from .env.
Troubleshooting
Chatto can't connect to NATS: Ensure NATS_TOKEN and CHATTO_NATS_CLIENT_TOKEN match in your .env file.
Registration says email delivery is not configured: Configure the CHATTO_SMTP_* settings in .env. Direct email/password registration sends a code by email.
The first account is not an owner: Ensure CHATTO_OWNERS_EMAILS contains that account's verified email address. Chatto assigns matching owner roles when the email is verified and on server boot.
Traefik not routing: Ensure TRAEFIK_NETWORK matches your external Traefik network name and CERTRESOLVER matches your Traefik certificate resolver. Verify the Chatto/LiveKit containers appear in docker network ls and are attached to the Traefik network.
Container startup order issues: The depends_on with condition: service_healthy ensures NATS and LiveKit are ready before Chatto starts.
Voice calls not working: Ensure the LiveKit API key/secret in .env matches the keys: section in the selected LiveKit config (livekit.generated.yaml or livekit.yaml). Also verify the webhook URL points to your Chatto instance. Make sure CHATTO_LIVEKIT_URL uses the public wss://livekit. subdomain (not the internal Docker hostname), since browsers connect to it directly.
LiveKit UDP ports: WebRTC requires UDP ports 50000-50200. Ensure your firewall allows inbound UDP on this range. These ports are exposed directly on the livekit container and are NOT proxied through Traefik.
Traefik WebSocket issues: Traefik v3 handles WebSocket upgrades automatically. If connections fail, check that the livekit. subdomain DNS resolves to your server and that Traefik has obtained a valid TLS certificate for it.