services: # --- Redis (Celery broker + result backend) --- redis: image: redis:7-alpine container_name: holy-redis command: redis-server --appendonly yes --save 60 1 restart: unless-stopped sysctls: - net.core.somaxconn=512 - vm.overcommit_memory=1 networks: - holy-net volumes: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 5 # --- Backend Service --- backend: build: context: ./backend container_name: holy-backend restart: unless-stopped networks: - holy-net ports: - 8000 env_file: - .env volumes: - ./backend:/app - ./legacy:/app/legacy environment: - PYTHONUNBUFFERED=1 - DOMAIN=${DOMAIN:-localhost} - DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE:-holy_backend.settings.prod} - CELERY_BROKER_URL=${CELERY_BROKER_URL:-redis://redis:6379/0} - CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND:-redis://redis:6379/0} depends_on: redis: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health/"] interval: 10s timeout: 5s retries: 5 start_period: 30s worker: build: context: ./backend container_name: holy-worker command: celery -A holy_backend worker -l info --concurrency=2 user: "app" restart: unless-stopped networks: - holy-net env_file: - .env environment: - PYTHONUNBUFFERED=1 - DOMAIN=${DOMAIN:-localhost} - DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE:-holy_backend.settings.prod} - CELERY_BROKER_URL=${CELERY_BROKER_URL:-redis://redis:6379/0} - CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND:-redis://redis:6379/0} - SKIP_INIT=true volumes: - ./backend:/app - ./legacy:/app/legacy depends_on: redis: condition: service_healthy backend: condition: service_healthy healthcheck: test: ["CMD-SHELL", "celery -A holy_backend inspect ping -d celery@$$HOSTNAME || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 30s beat: build: context: ./backend container_name: holy-beat command: celery -A holy_backend beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler --pidfile /tmp/celerybeat.pid user: "app" restart: unless-stopped networks: - holy-net env_file: - .env environment: - PYTHONUNBUFFERED=1 - DOMAIN=${DOMAIN:-localhost} - DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE:-holy_backend.settings.prod} - CELERY_BROKER_URL=${CELERY_BROKER_URL:-redis://redis:6379/0} - CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND:-redis://redis:6379/0} - SKIP_INIT=true volumes: - ./backend:/app - ./legacy:/app/legacy depends_on: redis: condition: service_healthy backend: condition: service_healthy healthcheck: test: ["CMD-SHELL", "test -f /tmp/celerybeat.pid && kill -0 $$(cat /tmp/celerybeat.pid) || exit 1"] interval: 30s timeout: 5s retries: 3 start_period: 15s flower: image: mher/flower:2.0 container_name: holy-flower restart: unless-stopped command: celery --broker=${CELERY_BROKER_URL:-redis://redis:6379/0} flower --port=5555 environment: - CELERY_BROKER_URL=${CELERY_BROKER_URL:-redis://redis:6379/0} - CELERY_RESULT_BACKEND=${CELERY_RESULT_BACKEND:-redis://redis:6379/0} - FLOWER_BASIC_AUTH=${FLOWER_BASIC_AUTH:-admin:change-me} ports: - 5555 networks: - holy-net depends_on: redis: condition: service_healthy frontend: build: context: ./frontend args: - VITE_API_URL=http://${DOMAIN:-localhost}/api/ - VITE_IMPRESSUM_NAME=${VITE_IMPRESSUM_NAME} - VITE_IMPRESSUM_STREET=${VITE_IMPRESSUM_STREET} - VITE_IMPRESSUM_CITY=${VITE_IMPRESSUM_CITY} - VITE_IMPRESSUM_EMAIL=${VITE_IMPRESSUM_EMAIL} - VITE_IMPRESSUM_PHONE=${VITE_IMPRESSUM_PHONE} container_name: holy-frontend restart: unless-stopped ports: - 80 networks: - holy-net env_file: - .env depends_on: backend: condition: service_healthy networks: holy-net: driver: bridge volumes: db_data: redis_data: