Docker Deployment
Services
The docker-compose.yml runs nine services:
| Service | Image | Port | Purpose |
|---|---|---|---|
| postgres | postgres:16-alpine | 5432 | Relational data (orgs, users, teams, integrations) |
| clickhouse | clickhouse/clickhouse-server:24-alpine | 8123 | Telemetry analytics (traces, metrics, logs) |
| redis | redis:7-alpine | 6379 | Caching |
| otel-collector | otel/opentelemetry-collector-contrib | 4317/4318 | Telemetry ingestion and routing |
| backend | Built from Dockerfile | 3001 | NestJS API server |
| frontend | Built from Dockerfile | 3000 | Next.js dashboard |
| openmemory | mem0/openmemory-mcp | 8765 | MCP memory server (persistent AI memory per developer) |
| qdrant | qdrant/qdrant | 6333 | Vector store for AI memory semantic search |
| watchtower | containrrr/watchtower | — | Automatic container updates from GitHub Releases |
Production Mode
cd ~/.tandemu # or wherever you cloned the repo
docker compose up --build -dThis builds the backend and frontend into optimized Docker images and runs everything.
Development Mode
For hot-reload during development:
docker compose -f docker-compose.yml -f docker-compose.dev.yml upThis:
- Starts infrastructure services (Postgres, ClickHouse, Redis, OTel Collector) normally
- Runs the backend with
tsc --watch+node --watch— restarts on TypeScript changes - Runs the frontend with
next dev— instant hot module replacement - Mounts source code as volumes so edits are picked up immediately
Persistent Data
Five named volumes store persistent data:
| Volume | Path in Container | Data |
|---|---|---|
postgres_data | /var/lib/postgresql/data | Organizations, users, teams, integrations |
clickhouse_data | /var/lib/clickhouse | All telemetry data |
redis_data | /data | Cache (ephemeral, safe to lose) |
openmemory_data | /app/data | Developer memories (personality, coding preferences) |
qdrant_data | /qdrant/storage | Vector embeddings for AI memory search |
To reset all data:
docker compose down -vThis removes all volumes. Next up starts fresh.
Updating
Tandemu uses Watchtower for automatic updates. When a new version is published as a GitHub Release, updated container images are pushed to GHCR. Watchtower detects the new images and restarts the affected containers automatically.
Automatic updates
Watchtower runs as a service in the Docker Compose stack. By default, it polls for new images every 24 hours. Only containers labeled with com.centurylinklabs.watchtower.scope=tandemu are updated — infrastructure services (Postgres, ClickHouse, Redis) are never touched.
No configuration is needed. Automatic updates work out of the box.
Dashboard update banner
When a new release is available, the dashboard shows an update banner with the current and latest version, plus a link to the release notes. Organization Owners and Admins can click Update Now to trigger an immediate update via Watchtower’s HTTP API — no SSH or CLI access required.
The banner checks for updates on page load and caches the result for one hour to avoid GitHub API rate limits. You can dismiss it per-version.
Manual updates
If you prefer to update manually or Watchtower is not running:
docker compose pull
docker compose up -dDatabase migrations are applied automatically when the backend starts. As a fallback if auto-migration fails:
for f in packages/database/src/migrations/*.sql; do
docker exec -i tandemu-postgres-1 psql -U tandemu -d tandemu < "$f"
doneDisabling automatic updates
To disable Watchtower, remove or comment out the watchtower service in docker-compose.yml. You can still use the dashboard update banner for one-click manual updates, or pull images yourself.
Health Checks
Postgres, ClickHouse, and Redis have built-in health checks. The backend and frontend wait for Postgres and Redis to be healthy before starting.
Check status:
docker compose psAll services should show Up or Up (healthy).