compose.yaml runs Xitter and PostgreSQL 18, with ffmpeg and pg_dump in the image. The client is downloaded into its volume on the first start.
Start
Edit PUBLIC_ORIGIN, HOST_PORT and the mail keys in .env. Any variable from Configuration can be added there.
Other scripts run the same way, for example:
Client builds
FETCH_CLIENT=0 skips the download on the first start.
- Re-pin the current build with:
Chat key backup
Add --profile keybackup to up to also run the Juicebox realm used for chat passcode recovery. It is built from deploy/juicebox with its own PostgreSQL. Its realm ID and tenant secret come from .env. See Encrypted chat.
Backups
Keep .env and back it up together with the volumes:
db
client
media
data
realm-db
data/ holds the instance’s federation key. Other instances stop trusting you if you lose it.
Health check
/healthz answers ok while the server runs and reaches its database. docker compose ps shows the app as unhealthy when it doesn’t.
Docker doesn’t restart unhealthy containers by itself. A watcher such as autoheal can.
Connection pooling
For an optional PgBouncer pool, add these to .env:
Then start with:
PgBouncer uses session pooling, so Bun’s prepared statements and the startup migration keep their connection semantics. PGBOUNCER_POOL_SIZE defaults to 20 server connections; tune it alongside DB_POOL_MAX for the number of app replicas. Backups use a direct PostgreSQL connection (BACKUP_DATABASE_URL).
The Compose PostgreSQL service uses conservative cache and autovacuum settings. Adjust them for the host’s memory and workload.