# Self-contained local development stack — Postgres + API + scheduler, all on this machine. # # Fully decoupled: its own local database (a Docker volume) and its own scheduler, so there # is no shared DB and no migration coupling with any other instance. Edit code, rebuild, and # everything (including the background sync) runs right here. # # docker compose -f docker-compose.localdev.yml up -d --build # # Browse at http://localhost:8080 — Google login works because the OAuth redirect is # http://localhost:8080/auth/callback. The API runs `alembic upgrade head` on startup, so it # applies local migrations to THIS local DB only. Seed the catalog once with a pg_dump from # another instance if you want real data (see RUNBOOK). services: db: image: postgres:16-alpine environment: POSTGRES_USER: ${POSTGRES_USER:-siftlode} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-siftlode} POSTGRES_DB: ${POSTGRES_DB:-siftlode} volumes: - pgdata:/var/lib/postgresql/data security_opt: - apparmor:unconfined healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-siftlode} -d ${POSTGRES_DB:-siftlode}"] interval: 5s timeout: 5s retries: 12 restart: unless-stopped api: build: context: . dockerfile: Dockerfile args: APP_VERSION: ${APP_VERSION:-dev} GIT_SHA: ${GIT_SHA:-unknown} BUILD_DATE: ${BUILD_DATE:-} env_file: .env environment: # Own local database (overrides whatever DATABASE_URL is in .env). DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-siftlode}:${POSTGRES_PASSWORD:-siftlode}@db:5432/${POSTGRES_DB:-siftlode} # This instance owns its scheduler now (its own DB, so no double-write concern). SCHEDULER_ENABLED: "true" depends_on: db: condition: service_healthy # Harmless on Docker Desktop; needed on some nested/hardened Docker hosts. security_opt: - apparmor:unconfined ports: - "${APP_PORT:-8080}:8000" restart: unless-stopped volumes: pgdata: