# Docker Compose configuration for Lychee application with FrankenPHP backend # Version: 2026-01-10 x-base-lychee-setup: &base-lychee-setup image: ghcr.io/lycheeorg/lychee:latest restart: unless-stopped # Auto-restart at container level (outer layer) # Security hardening security_opt: - no-new-privileges:true - seccomp:unconfined # FrankenPHP may need this; consider custom seccomp profile cap_drop: - ALL cap_add: - CHOWN - SETGID - SETUID - DAC_OVERRIDE - NET_BIND_SERVICE read_only: false # Laravel needs write access to storage/cache tmpfs: - /tmp:noexec,nosuid,nodev,size=100m volumes: - ./lychee/uploads:/app/public/uploads - ./lychee/logs:/app/storage/logs - ./lychee/tmp:/app/storage/tmp networks: - lychee x-common-env: &common-env PUID: "${PUID:-1000}" PGID: "${PGID:-1000}" # REPLACE ME WITH YOUR VALUE GENERATED WITH `openssl rand -base64 32` APP_KEY: "base64:ucMiFCbfQZUFOxrQz1x9C0OBJeLCCCAieZmnEHXmyGI=" APP_NAME: "${APP_NAME:-Lychee}" APP_ENV: "${APP_ENV:-production}" APP_TIMEZONE: "${TIMEZONE:-UTC}" APP_URL: "${APP_URL:-http://localhost:8000}" APP_FORCE_HTTPS: "${APP_FORCE_HTTPS:-false}" DB_CONNECTION: "${DB_CONNECTION:-mysql}" DB_HOST: "${DB_HOST:-lychee_db}" DB_PORT: "${DB_PORT:-3306}" DB_DATABASE: "${DB_DATABASE:-lychee}" DB_USERNAME: "${DB_USERNAME:-lychee}" DB_PASSWORD: "${DB_PASSWORD:-password}" SESSION_DRIVER: "${SESSION_DRIVER:-file}" SESSION_LIFETIME: "${SESSION_LIFETIME:-120}" QUEUE_CONNECTION: "${QUEUE_CONNECTION:-database}" AI_VISION_ENABLED: "${AI_VISION_ENABLED:-true}" AI_VISION_FACE_API_KEY: "${AI_VISION_API_KEY:-changeme}" AI_VISION_FACE_URL: "http://lychee_facial_recognition:8000" services: lychee_api: <<: *base-lychee-setup container_name: lychee-api expose: - "${APP_PORT:-8000}" ports: - "${APP_PORT:-8000}:8000" environment: <<: *common-env # Enable WORKER MODE depends_on: lychee_db: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/up"] interval: 10s timeout: 5s retries: 5 start_period: 30s lychee_worker: <<: *base-lychee-setup container_name: lychee-worker environment: <<: *common-env # Enable WORKER MODE LYCHEE_MODE: worker depends_on: lychee_db: condition: service_healthy lychee_api: condition: service_healthy healthcheck: test: ["CMD-SHELL", "pgrep -f 'queue:work' || exit 1"] interval: 30s timeout: 10s retries: 3 start_period: 60s # Give worker time to start up lychee_db: image: mariadb:10 security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - SETGID - SETUID - DAC_OVERRIDE - CHOWN read_only: false # MariaDB needs write access tmpfs: - /tmp:noexec,nosuid,nodev,size=200m - /var/run/mysqld:noexec,nosuid,nodev,size=10m environment: - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD:-rootpassword} - MYSQL_DATABASE=${DB_DATABASE:-lychee} - MYSQL_USER=${DB_USERNAME:-lychee} - MYSQL_PASSWORD=${DB_PASSWORD:-password} expose: - 3306 volumes: - mysql:/var/lib/mysql networks: - lychee restart: unless-stopped healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 5s timeout: 3s retries: 10 start_period: 10s lychee_facial_recognition: container_name: lychee-facial-recognition image: ghcr.io/lycheeorg/lychee-facial-recognition:latest restart: unless-stopped security_opt: - no-new-privileges:true cap_drop: - ALL environment: VISION_FACE_LYCHEE_API_URL: "http://lychee_api:8000" VISION_FACE_API_KEY: "${AI_VISION_API_KEY:-changeme}" VISION_FACE_VERIFY_SSL: "${AI_VISION_VERIFY_SSL:-true}" VISION_FACE_PHOTOS_PATH: "/data/photos" VISION_FACE_STORAGE_PATH: "/data/embeddings" VISION_FACE_WORKERS: "${AI_VISION_WORKERS:-1}" volumes: - ./lychee/uploads:/data/photos:ro - ai_vision_embeddings:/data/embeddings networks: - lychee depends_on: lychee_api: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 3 start_period: 60s networks: lychee: volumes: mysql: name: lychee_prod_mysql driver: local ai_vision_embeddings: name: lychee_ai_vision_embeddings driver: local