services: db: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_USER: mirrai POSTGRES_PASSWORD: mirrai POSTGRES_DB: mirrai volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U mirrai"] interval: 5s timeout: 5s retries: 12 redis: image: redis:7-alpine restart: unless-stopped # The limits and the scheduler lock live here, so every copy of the app # counts against the same numbers. The app talks to it without anybody # having to write down an address. command: ["redis-server", "--save", "", "--appendonly", "no"] healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 12 app: build: . restart: unless-stopped ports: - "3000:3000" env_file: .env environment: DATABASE_URL: postgresql://mirrai:mirrai@db:5432/mirrai REDIS_URL: redis://redis:6379 # The first-run administrator's password is written here, and it # has to survive the container being rebuilt. # # The data root used to fall back to the parent of the uploads # directory — which is inside the container's writable layer, so # `docker compose up --build` threw the operator's only copy of the # generated password away. MIRRAI_DATA_ROOT: /app/data volumes: - uploads:/app/uploads - data:/app/data depends_on: db: condition: service_healthy redis: condition: service_healthy volumes: pgdata: uploads: data: