# Production-style deployment with Docker Compose: PostgreSQL, a one-off # migration step, and the web app. Configuration comes from .env # (copy .env.example). See docker.md. # # cp .env.example .env # then fill in the required values # docker compose up -d --build # # For local development use docker-compose.dev.yml instead. x-app: &app build: . image: ungleich-user:latest env_file: ${ENV_FILE:-.env} environment: DATABASE_HOST: db read_only: true tmpfs: - /tmp cap_drop: [ALL] security_opt: ["no-new-privileges:true"] services: db: image: postgres:18-alpine restart: unless-stopped environment: POSTGRES_DB: ${DATABASE_NAME:-ungleich_user} POSTGRES_USER: ${DATABASE_USER:-ungleich_user} POSTGRES_PASSWORD: ${DATABASE_PASSWORD:?set DATABASE_PASSWORD in .env} volumes: - pgdata:/var/lib/postgresql healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"] interval: 3s retries: 20 # Runs once per `up` and exits; web starts only if it succeeded. migrate: <<: *app command: ["python", "manage.py", "migrate_locked"] restart: "no" depends_on: db: condition: service_healthy web: <<: *app restart: unless-stopped ports: - "${PORT:-8000}:8000" depends_on: migrate: condition: service_completed_successfully healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/healthz', timeout=3)"] interval: 15s timeout: 5s retries: 3 volumes: pgdata: