name: supabase-vps-container-recovery
description: Recover Supabase self-hosted (deploy-vps) containers when they fail to authenticate to the database after a docker compose operation. Fixes SCRAM-SHA-256 auth failures and JWT mismatches.
triggers:
- container restarting with "password authentication failed for user postgres"
- auth/rest/storage containers in restart loop after docker compose up
- JWSInvalidSignature errors on login
Quick Diagnosis
`bash
Check container status
docker ps -a --filter 'name=deploy-vps' --format '{{.Names}} {{.Status}}'
Check auth logs
docker logs deploy-vps-auth-1 --tail 5
`
Common Causes
1. SCRAM-SHA-256 pg_hba.conf conflict: host all all all scram-sha-256 is evaluated before more specific rules, blocking Docker network connections
2. JWT secret mismatch: GoTrue (GOTRUE_JWT_SECRET) different from PostgREST (PGRST_JWT_SECRET)
3. Container desync after compose: docker compose up -d recreates only that service with new env vars, but DB credentials may not match
Recovery Procedure
Step 1 — Fix pg_hba.conf (if auth can't connect)
`bash
Remove scram-sha-256 rule that's blocking Docker network connections
docker exec deploy-vps-db-1 sh -c 'sed -i "/host all all all scram-sha-256/d" /var/lib/postgresql/data/pg_hba.conf && psql -U postgres -c "SELECT pg_reload_conf();"'
`
Step 2 — Get actual credentials from .env
`bash
Read JWT secret (real value, not masked)
python3 -c "import os; d=open('/docker/deploy-vps/.env').read(); [print(k,v) for l in d.splitlines() for k,v in [l.split('=',1)] if k in ['POSTGRES_PASSWORD','JWT_SECRET','PGRST_JWT_SECRET']]"
Read postgres password directly from running DB container
docker exec deploy-vps-db-1 sh -c 'echo "$POSTGRES_PASSWORD"'
`
Step 3 — Recreate auth container manually
`bash
docker rm -f deploy-vps-auth-1
docker run -d \
--name deploy-vps-auth-1 \
--network deploy-vps_default \
-p 9999:9999 \
--restart unless-stopped \
-e GOTRUE_API_HOST=0.0.0.0 \
-e GOTRUE_API_PORT=9999 \
-e GOTRUE_DB_DRIVER=postgres \
-e GOTRUE_DB_DATABASE_URL="postgres://postgres:{POSTGRES_PASSWORD}@db:5432/postgres" \
-e GOTRUE_DB_SCHEMA=auth \
-e GOTRUE_DB_SEARCH_PATH=auth,public \
-e GOTRUE_SITE_URL=https://rochasalesseguros.com.br \
-e GOTRUE_URI_ALLOW_LIST='' \
-e GOTRUE_DISABLE_SIGNUP=false \
-e GOTRUE_JWT_SECRET={JWT_SECRET} \
-e GOTRUE_JWT_EXP=3600 \
-e GOTRUE_EXTERNAL_EMAIL_ENABLED=true \
-e GOTRUE_MAILER_AUTOCONFIRM=true \
-e API_EXTERNAL_URL=https://rochasalesseguros.com.br \
supabase/gotrue:v2.99.0
`
Step 4 — Recreate rest container manually
`bash
docker rm -f deploy-vps-rest-1
docker run -d \
--name deploy-vps-rest-1 \
--network deploy-vps_default \
-p 3002:3000 \
--restart unless-stopped \
-e PGRST_DB_URI="postgres://postgres:{POSTGRES_PASSWORD}@db:5432/postgres" \
-e PGRST_JWT_SECRET={JWT_SECRET} \
-e PGRST_DB_SCHEMA=public \
-e PGRST_DB_ANON_ROLE=anon \
-e PGRST_VERIFY_JWT=false \
-e DB_INSTALL=false \
postgrest/postgrest:v12.2.3
`
Step 5 — Verify auth + rest are connected
`bash
Auth health
curl http://
Rest connection
docker logs deploy-vps-rest-1 --tail 3
`
Step 6 — Create admin user if auth.users is empty
`bash
Generate bcrypt hash
python3 -c "import bcrypt; print(bcrypt.hashpw(b'221109', bcrypt.gensalt(10)).decode())"
Insert admin user
docker exec deploy-vps-db-1 psql -U postgres -d postgres -c "
INSERT INTO auth.users (id, instance_id, email, encrypted_password, email_confirmed_at, created_at, updated_at, raw_user_meta_data, raw_app_meta_data, last_sign_in_at, role, mfa_challenges, mfa_factors, deleted_at)
VALUES (
gen_random_uuid(),
'00000000-0000-0000-0000-000000000000',
'admin@rochasalesseguros.com.br',
'\$2b\$10\$IO0ia7dvOrSJjW7S1Vom4eRWKZ.R5t7PKya4PIa.TLR6ScZvqaOJq',
now(), now(), now(),
'{\"first_name\":\"Admin\",\"last_name\":\"Rocha Sales\"}',
'{\"provider\":\"email\",\"providers\":[\"email\"]}',
now(), 'authenticated', '{}', '{}', NULL
);"
`
Key Lessons
- - Never use
docker compose up -don Supabase stacks — it desyncs container credentials - - Use
docker compose down && up -dfor full stack restarts, ordocker runwith explicit env vars for targeted recovery - - JWT secrets must match:
GOTRUE_JWT_SECRET = PGRST_JWT_SECRET - - Read real env var values from running containers, not from masked shell output
- - Storage API requires:
DATABASE_URL,PGRST_JWT_SECRET,FILE_SIZE_LIMIT,STORAGE_BACKEND,FILE_STORAGE_BACKEND_PATH