Docker Deployment
Exocortex ships a docker-compose.yml that starts the full stack — database, capture API, workers, and optional Telegram bot — with a single command.
Prerequisites
- Docker Engine ≥ 24 and Docker Compose ≥ 2.20
- 4 GB RAM (8 GB recommended for the LLM synthesis step)
- First build takes ~3–5 min (Apache AGE compiles from source). Subsequent builds use the layer cache.
Quickstart
git clone https://github.com/hretheum/exocortex.gitcd exocortex
# Copy and customise the override templatecp docker-compose.override.yml.example docker-compose.override.yml# Edit docker-compose.override.yml: set EXOCORTEX_VAULT_PATH
docker compose up -d --buildcurl localhost:8000/health # → {"status":"ok"}After a successful start, docker compose ps should show 7+ running containers:
db, api, notify-listener, scorer, vault-watcher, scheduler, and any profile services.
Services
| Service | Description | Profile |
|---|---|---|
db | Postgres 16 + pgvector + Apache AGE | always |
api | Capture API (FastAPI, port 8000) | always |
notify-listener | pg_notify → Telegram push daemon | always |
scorer | Content acquisition scorer (F6.3) | always |
vault-watcher | Vault file watcher → /capture | always |
scheduler | supercronic — ingest/synth/compile crons | always |
telegram-bot | Telegram bot (polling) | --profile telegram |
syncthing | Bidirectional vault sync | --profile full |
Environment variables
Minimal required in .env (or docker-compose.override.yml):
| Variable | Default | Description |
|---|---|---|
EXOCORTEX_VAULT_PATH | /opt/exocortex-vault | Host path to your Obsidian vault |
TENANT_ID | 00000000-...0001 | Your tenant UUID |
CAPTURE_API_TOKEN | dev-token | Auth token for /capture endpoint |
ANTHROPIC_API_KEY | (empty) | Required for LLM synthesis |
DEEPINFRA_API_KEY | (empty) | Optional LLM fallback |
Pre-built database image
The db service builds Apache AGE from source (~2 min). To skip this on every fresh install, pull the pre-built image:
docker pull ghcr.io/hretheum/exocortex-db:latestThen edit docker-compose.yml (or docker-compose.override.yml) and change the db service to use:
services: db: image: ghcr.io/hretheum/exocortex-db:latest # remove the build: blockOptional profiles
Telegram bot
# Set in .env or docker-compose.override.yml:TG_BOT_TOKEN=1234567890:AABBCCdd...TG_ALLOWED_USER_IDS=123456789
docker compose --profile telegram up -d telegram-botSyncthing
docker compose --profile full up -d syncthing# Syncthing UI: http://localhost:8384Scheduler (supercronic)
The scheduler service runs cron jobs inside the container without a cron daemon:
| Schedule | Command |
|---|---|
Every hour :00 | exocortex ingest --from $EXOCORTEX_VAULT_PATH |
Every hour :30 | python -m exocortex.rss_ingester --once |
| 03:00 UTC daily | exocortex synth --all |
| 04:00 UTC daily | exocortex compile --domain all |
| Every 15 min | python -m exocortex.live_sections --once |
Logs: docker logs scheduler
Health checks
# All servicesdocker compose ps
# Capture APIcurl localhost:8000/health
# Databasedocker compose exec db pg_isready -U exocortex -d exocortexStopping
docker compose down # stop containers, keep volumes (data preserved)docker compose down -v # stop + wipe all volumes (fresh start)Troubleshooting
Port 5432 already in use
# Check what's using the portlsof -i :5432# Change the mapped port in docker-compose.override.yml:# ports: ["15432:5432"]API container starts before migrations finish
The api container runs exocortex migrate up before starting uvicorn. If migrations fail, the container exits with a non-zero code. Check docker logs api for details.
Vault watcher not picking up files
Make sure EXOCORTEX_VAULT_PATH is set to the same path in all worker services and the volume is correctly mounted.