services: neta-web: build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL:?Set NEXT_PUBLIC_SUPABASE_URL in .env} NEXT_PUBLIC_SUPABASE_ANON_KEY: ${NEXT_PUBLIC_SUPABASE_ANON_KEY:?Set NEXT_PUBLIC_SUPABASE_ANON_KEY in .env} NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-http://localhost:3000} container_name: neta-web restart: unless-stopped depends_on: neta-migrations: condition: service_completed_successfully neta-supabase-proxy: condition: service_healthy ports: - "${NETA_PORT:-3000}:3000" environment: NODE_ENV: production NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL:?Set NEXT_PUBLIC_SUPABASE_URL in .env} NEXT_PUBLIC_SUPABASE_ANON_KEY: ${NEXT_PUBLIC_SUPABASE_ANON_KEY:?Set NEXT_PUBLIC_SUPABASE_ANON_KEY in .env} SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_ROLE_KEY:?Set SUPABASE_SERVICE_ROLE_KEY in .env} SUPABASE_INTERNAL_URL: http://neta-supabase-proxy:8000 NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-http://localhost:3000} neta-supabase-proxy: image: nginx:1.27-alpine container_name: neta-supabase-proxy restart: unless-stopped depends_on: neta-auth: condition: service_started neta-rest: condition: service_started neta-storage: condition: service_started ports: - "${SUPABASE_API_PORT:-8000}:8000" volumes: - ./deploy/supabase/nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro healthcheck: test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8000/health >/dev/null"] interval: 10s timeout: 5s retries: 12 neta-auth: image: supabase/gotrue:v2.189.0 container_name: neta-auth restart: unless-stopped depends_on: neta-db: condition: service_healthy environment: GOTRUE_API_HOST: 0.0.0.0 GOTRUE_API_PORT: 9999 API_EXTERNAL_URL: ${NEXT_PUBLIC_SUPABASE_URL:?Set NEXT_PUBLIC_SUPABASE_URL in .env}/auth/v1 GOTRUE_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-http://localhost:3000} GOTRUE_URI_ALLOW_LIST: ${NEXT_PUBLIC_SITE_URL:-http://localhost:3000} GOTRUE_DB_DRIVER: postgres GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@neta-db:5432/postgres GOTRUE_JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} GOTRUE_JWT_EXP: ${JWT_EXPIRY:-3600} GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated GOTRUE_DISABLE_SIGNUP: "false" GOTRUE_EXTERNAL_EMAIL_ENABLED: "true" GOTRUE_MAILER_AUTOCONFIRM: "true" GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL:-admin@neta.local} GOTRUE_SMTP_HOST: ${SMTP_HOST:-} GOTRUE_SMTP_PORT: ${SMTP_PORT:-587} GOTRUE_SMTP_USER: ${SMTP_USER:-} GOTRUE_SMTP_PASS: ${SMTP_PASS:-} GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME:-Neta} neta-rest: image: postgrest/postgrest:v12.2.12 container_name: neta-rest restart: unless-stopped depends_on: neta-db: condition: service_healthy environment: PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@neta-db:5432/postgres PGRST_DB_SCHEMAS: public,storage PGRST_DB_ANON_ROLE: anon PGRST_JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} PGRST_DB_USE_LEGACY_GUCS: "false" neta-storage: image: supabase/storage-api:v1.24.7 container_name: neta-storage restart: unless-stopped depends_on: neta-db: condition: service_healthy neta-rest: condition: service_started environment: ANON_KEY: ${NEXT_PUBLIC_SUPABASE_ANON_KEY:?Set NEXT_PUBLIC_SUPABASE_ANON_KEY in .env} SERVICE_KEY: ${SUPABASE_SERVICE_ROLE_KEY:?Set SUPABASE_SERVICE_ROLE_KEY in .env} POSTGREST_URL: http://neta-rest:3000 PGRST_JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@neta-db:5432/postgres FILE_SIZE_LIMIT: ${STORAGE_FILE_SIZE_LIMIT:-52428800} STORAGE_BACKEND: file FILE_STORAGE_BACKEND_PATH: /var/lib/storage TENANT_ID: stub REGION: local GLOBAL_S3_BUCKET: stub ENABLE_IMAGE_TRANSFORMATION: "false" volumes: - neta-storage-data:/var/lib/storage neta-migrations: image: postgres:16-alpine container_name: neta-migrations depends_on: neta-db: condition: service_healthy neta-storage: condition: service_started environment: DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@neta-db:5432/postgres volumes: - ./supabase:/app/supabase:ro - ./scripts:/app/scripts:ro working_dir: /app command: - sh - -c - | tries=0 until psql "$$DATABASE_URL" -v ON_ERROR_STOP=1 -tAc "select case when to_regclass('auth.users') is not null and to_regclass('storage.buckets') is not null then 'ready' else 'waiting' end" | grep -q "ready"; do tries=$$((tries + 1)) if [ "$$tries" -gt 60 ]; then echo "Timed out waiting for Supabase Auth/Storage schemas." exit 1 fi echo "Waiting for Supabase Auth/Storage schemas..." sleep 2 done sh ./scripts/apply-migrations.sh neta-db: image: pgvector/pgvector:pg16 container_name: neta-db restart: unless-stopped ports: - "${POSTGRES_PORT:-54322}:5432" environment: POSTGRES_DB: postgres POSTGRES_USER: postgres POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env} POSTGRES_INITDB_ARGS: --auth-host=scram-sha-256 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] interval: 5s timeout: 5s retries: 20 volumes: - neta-db-data:/var/lib/postgresql/data - ./deploy/supabase/db/init.sh:/docker-entrypoint-initdb.d/00-neta-init.sh:ro volumes: neta-db-data: neta-storage-data: