services: app: build: . env_file: .env environment: DATABASE_URL: postgres://krona_staff_app:${POSTGRES_APP_PASSWORD:-local-app-only}@postgres:5432/krona_staff ports: ['8080:8080'] depends_on: postgres: { condition: service_healthy } restart: unless-stopped read_only: true tmpfs: [/tmp] security_opt: ['no-new-privileges:true'] migrate: build: { context: ., target: build } env_file: .env environment: DATABASE_MIGRATION_URL: postgres://krona_migrator:${POSTGRES_MIGRATION_PASSWORD:-local-migration-only}@postgres:5432/krona_staff command: ['node', 'scripts/migrate.mjs', 'up'] depends_on: postgres: { condition: service_healthy } profiles: ['tools'] postgres: image: postgres:17-alpine@sha256:18cfe3ef5e6815560c98237d6216d1e5119702fb0f3894c8785dd58b8bbe5d73 environment: POSTGRES_DB: krona_staff POSTGRES_USER: krona_migrator POSTGRES_PASSWORD: ${POSTGRES_MIGRATION_PASSWORD:-local-migration-only} POSTGRES_APP_PASSWORD: ${POSTGRES_APP_PASSWORD:-local-app-only} volumes: - postgres-data:/var/lib/postgresql/data - ./docker/postgres/init-app-role.sh:/docker-entrypoint-initdb.d/10-app-role.sh:ro healthcheck: { test: ['CMD-SHELL', 'pg_isready -U krona_migrator -d krona_staff'], interval: 5s, timeout: 3s, retries: 20, } volumes: postgres-data: