Docker Compose
What the production compose stack deploys and how to customise it.
The docker-compose.yml we publish is the reference deployment. It boots Cariosan plus every service it depends on, with sensible healthchecks and depends_on gates so docker compose up -d waits until the stack is actually ready.
What it runs
| Service | Image | Purpose |
|---|---|---|
cariosan-server | ghcr.io/cariosan/server:latest | The Go binary. Applies migrations on boot. |
postgres | postgres:15-alpine | Messages, users, channels, webhooks. |
redis | redis:7-alpine | Rate limiting, presence, pub/sub fanout. |
minio | minio/minio:latest | S3-compatible store for attachments. |
minio-bootstrap | minio/mc:latest | One-shot: creates the bucket with a public-read policy, then exits. |
All services live on an isolated cariosan network. Only the Cariosan HTTP port (:8080) and the MinIO S3 API (:9000, needed so browsers can reach presigned URLs) are published to the host.
Why MinIO is exposed — browsers PUT attachments directly to S3 via presigned URLs (the bytes never proxy through Cariosan). The MinIO endpoint must be reachable from end users for this to work.
Pin a specific version
The default CARIOSAN_IMAGE=ghcr.io/cariosan/server:latest floats on the most recent release.
Pin in production — :latest is convenient for staging but a surprise in prod. Tag a specific semver so rollbacks (docker compose pull && up -d) are deterministic.
See the operations guide for the upgrade and rollback loop.
Using an external Postgres or S3
Most teams eventually run managed Postgres (RDS, Neon, Supabase) instead of the bundled container. Delete the postgres service from compose and point CARIOS_DATABASE_URL at your managed instance:
For S3 → AWS S3 (or any S3-compatible service), remove the minio and minio-bootstrap services and supply credentials:
CARIOS_S3_ENDPOINT= (empty) tells the AWS SDK to use the default region endpoint. The IAM user needs s3:PutObject + s3:GetObject on the bucket.
Resource sizing
The server is stateless and light: idle RSS is under 40 MB, and a single CPU comfortably handles up to a few thousand connected WebSockets. Bottlenecks are Postgres and Redis first.
For a VPS-scale deployment, start with:
- Server: 0.5 vCPU / 512 MB RAM
- Postgres: 1 vCPU / 1 GB RAM, 20 GB SSD
- Redis: 0.25 vCPU / 256 MB RAM
- MinIO: 0.25 vCPU / 256 MB RAM, 10+ GB disk (grows with attachments)
Scale horizontally — run multiple cariosan-server containers behind a sticky-session-free load balancer. Redis pub/sub handles the cross-instance WebSocket fanout. See performance & scaling for sizing tiers.
TLS
Cariosan speaks plain HTTP on purpose — terminate TLS at whatever reverse proxy you already trust (Caddy, nginx, Traefik, a cloud load balancer).
WebSocket upgrade — browsers need wss:// for WebSockets, so the reverse proxy must pass the upgrade handshake. Caddy does this by default; nginx requires the Upgrade/Connection headers above. If clients fail to connect with 101 errors, the proxy is the first place to check.
Was this page helpful?