Troubleshooting
When something doesn't come up, start by asking compose what state it thinks each container is in:
docker compose ps
Look for unhealthy or exited (1) — those are the failing
services. Tail their logs:
docker compose logs --tail=100 <service>
The most common failure modes follow.
web is unhealthy / /api/health times out
90% of the time this means migrations didn't finish. Check:
docker compose logs --tail=50 db-migrate
Expected end-state:
[db-migrate] Done. Applied 320, skipped 0 (already up to date).
If you see a psql: error: connection refused, the db container
isn't fully healthy yet — wait 30 seconds and look again. If a SQL
file failed mid-replay, the file name appears in the log; file a
GitHub issue with the offending file + the error message.
Ollama container restarts forever
The host doesn't have enough RAM for the model. The default
llama3.1:8b-instruct-q4_K_M needs ~6 GB warm; if you're at 8 GB
total host RAM, the data plane + app services + model don't fit.
Swap to a smaller model:
echo 'AI_OLLAMA_MODEL=phi3:mini' >> .env
docker compose up -d --force-recreate ai-services ollama-preload
phi3:mini is ~2.3 GB on disk + ~3 GB warm. Tool-call quality drops
vs Llama 3.1 8B, but agent loops still work for evaluation.
Port already in use
If you see bind: address already in use on up:
docker compose down
# Find what's holding 3000 / 8000 / 5432 / 11434
ss -ltn | grep -E ':3000|:8000|:5432|:11434'
Either stop the holding process or override the port in .env:
CCD_PORT=3010
KONG_HTTP_PORT=8010
POSTGRES_PORT=5433
Then docker compose up -d --force-recreate.
auth container exits immediately
Almost always a JWT_SECRET / ANON_KEY / SERVICE_ROLE_KEY
mismatch — the keys must be JWTs signed with the same JWT_SECRET.
If you edited JWT_SECRET by hand, regenerate the matching keys:
./ccd keys regenerate
# This refreshes POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY,
# SERVICE_ROLE_KEY, REALTIME_SECRET_KEY_BASE in .env, then:
docker compose up -d --force-recreate
The install.sh script does this for you on first run.
realtime container won't start (Erlang error)
The REALTIME_SECRET_KEY_BASE needs to be 64+ chars. If you
regenerated keys manually and copied less, Phoenix dies on boot.
./ccd keys regenerate emits 86 chars — use that rather than
hand-rolling.
Storage uploads fail with 403
Two checks:
-
SUPABASE_ANON_KEY/SUPABASE_SERVICE_ROLE_KEYare signed with the sameJWT_SECRETthat the storage container has. -
The
authenticatedrole can write to the bucket. Check the bucket's RLS policies in the admin portal (cloud) or via SQL:SELECT * FROM storage.buckets; SELECT polname, polqual FROM pg_policy WHERE polrelid = 'storage.objects'::regclass;
Migration replay fails on re-install
If docker compose down -v then re-install errors during
db-migrate, the most likely cause is a partial volume wipe — the
postgres volume ccd-supabase-db-data is gone but the storage
volume ccd-supabase-storage-data survived (or vice versa). Wipe
both:
docker volume rm ccd-supabase-db-data ccd-supabase-storage-data
docker compose up -d
worker runs but no jobs process
Check the BullMQ Redis connection. If REDIS_URL is unset, the
worker boots but can't pop jobs:
docker compose logs --tail=50 worker | grep -i redis
Self-host doesn't bundle Redis — set REDIS_URL=redis://your-redis:6379
in .env or add a redis: image: redis:7-alpine service to
docker-compose.yml.
"I need help" — what to include
If you file a GitHub issue, paste:
docker version
docker compose version
docker compose ps
docker compose logs --tail=200 <failing-service> 2>&1
sed 's|=.*|=<redacted>|' .env | grep -E '^[A-Z_]+=' # var names only
The last line shows which .env variables are set (without leaking
their values) — important when diagnosing config issues.