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:

  1. SUPABASE_ANON_KEY / SUPABASE_SERVICE_ROLE_KEY are signed with the same JWT_SECRET that the storage container has.

  2. The authenticated role 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.