# Base / production config. Run alone with: docker compose -f compose.yml up # Plain `docker compose up` also merges compose.override.yml for development. services: web: build: . ports: - "3000:3000" # Explicit behaviour toggles (the app is environment-agnostic — see AGENTS.md). # Supply COOKIE_SECRET / CSRF_SECRET via env; REQUIRE_SECURE_SECRETS refuses dev throwaways. environment: CACHE_TEMPLATES: "true" REQUIRE_SECURE_SECRETS: "true" restart: unless-stopped # Ory's storage only (Kratos/Keto/Hydra) — the web app never connects here. # init/init.sql creates one database per service. Dev defaults below; supply # POSTGRES_USER/PASSWORD via env in production. postgres: image: postgres:18.4-alpine3.23 environment: POSTGRES_USER: ${POSTGRES_USER:-ory} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ory} POSTGRES_DB: ory volumes: - ./ory/postgres/init:/docker-entrypoint-initdb.d:ro - pgdata:/var/lib/postgresql # PG18+: mount the parent, not /data (version-subdir layout) healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-ory} -d ory"] interval: 5s timeout: 5s retries: 10 restart: unless-stopped # Ory Kratos — identity & self-service auth. Config + identity schema in ory/kratos/. # DSN is the per-service `kratos` DB (init.sql); supply POSTGRES_* via env in prod. kratos-migrate: image: oryd/kratos:v26.2.0 depends_on: postgres: condition: service_healthy environment: DSN: postgres://${POSTGRES_USER:-ory}:${POSTGRES_PASSWORD:-ory}@postgres:5432/kratos?sslmode=disable volumes: - ./ory/kratos:/etc/config/kratos:ro command: -c /etc/config/kratos/kratos.yml migrate sql -e --yes restart: on-failure kratos: image: oryd/kratos:v26.2.0 depends_on: kratos-migrate: condition: service_completed_successfully environment: DSN: postgres://${POSTGRES_USER:-ory}:${POSTGRES_PASSWORD:-ory}@postgres:5432/kratos?sslmode=disable volumes: - ./ory/kratos:/etc/config/kratos:ro command: serve -c /etc/config/kratos/kratos.yml restart: unless-stopped volumes: pgdata: