OpenLogs

All tools

Lychee

Photo management web app for uploading, organizing and sharing albums.

Replaces

Paid photo hosting services, such as Flickr.

All open-source alternatives to Flickr

Difficulty

Difficulty: Hard

4 services (1 database or broker: mariadb), 6 volume mounts, no env values to set, host access: cap_add. Heuristic score 6 of 11: hard.

Stack

PHP (language), MariaDB (database)

Deployment

docker_compose

Requirements

Not stated.

License

MIT (OSI-approved open source)

Read from github.com/LycheeOrg/Lychee/blob/d7deef4/LICENSE

Business model

Unknown

The README at the pinned commit does not state a business model, so it stays unknown.

Source: github.com/LycheeOrg/Lychee/blob/d7deef4/README.md, read on .

Repository

LycheeOrg/Lychee

GitHub stats

Stars
4,312
Forks
386
Open issues and pull requests
18
Last push
2026-10-22
Latest release
v7.11.1
Released
2026-10-13

From the GitHub API on . These numbers are a snapshot, not live. GitHub detected the license as MIT.

Compose file

Config check passed docker compose config exited 0 on this exact file on 2026-10-22.
docker-compose.minimal.yaml Version 1
# 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

From the app's own repository: github.com/LycheeOrg/Lychee/blob/d7deef4/docker-compose.minimal.yaml.

Compose file from the app repository.

Install

  1. Docker with the Compose plugin (github.com/LycheeOrg/Lychee/blob/d7deef4/README.md)

    A host with Docker and the Docker Compose plugin. This entry runs from a compose file.

  2. Save the compose file as docker-compose.minimal.yaml (github.com/LycheeOrg/Lychee/blob/d7deef4/docker-compose.minimal.yaml)

    Use the compose file shown above.

  3. What the compose file check ran with (github.com/LycheeOrg/Lychee/blob/d7deef4/README.md)

    The compose file check ran on this file alone. The folder held only this file. The README tells the reader to download the minimal docker-compose template and start it with docker compose -f docker-compose.minimal.yaml up -d.

  4. Start it (github.com/LycheeOrg/Lychee/blob/d7deef4/README.md)
    docker compose -f docker-compose.minimal.yaml up -d
  5. Open the app (github.com/LycheeOrg/Lychee/blob/d7deef4/docker-compose.minimal.yaml)

    Open http://localhost:8000. The compose file publishes host port 8000 for the lychee_api service. The README says to open this address in a browser.

Compose file check: config_ok on 2026-10-22.

Gotchas and maintenance

Gotchas

  • Some images use the latest tag or no tag. Images without a pinned version: lychee_api: ghcr.io/lycheeorg/lychee:latest, lychee_worker: ghcr.io/lycheeorg/lychee:latest, lychee_facial_recognition: ghcr.io/lycheeorg/lychee-facial-recognition:latest. A later pull can get a different version. (github.com/LycheeOrg/Lychee/blob/d7deef4/docker-compose.minimal.yaml, checked 2026-10-22)
  • The compose file has placeholder values. Placeholder values: DB_PASSWORD is "password" (lychee_api), AI_VISION_FACE_API_KEY is "changeme" (lychee_api), DB_PASSWORD is "password" (lychee_worker), AI_VISION_FACE_API_KEY is "changeme" (lychee_worker), MYSQL_PASSWORD is "password" (lychee_db), VISION_FACE_API_KEY is "changeme" (lychee_facial_recognition). (github.com/LycheeOrg/Lychee/blob/d7deef4/docker-compose.minimal.yaml, checked 2026-10-22)
  • The compose file publishes host ports. Host ports: 8000 (lychee_api). A port without a host address listens on all host network interfaces. (github.com/LycheeOrg/Lychee/blob/d7deef4/docker-compose.minimal.yaml, checked 2026-10-22)
  • The compose file mounts host folders. Host paths mounted into containers: ./lychee/uploads at /app/public/uploads (lychee_api), ./lychee/logs at /app/storage/logs (lychee_api), ./lychee/tmp at /app/storage/tmp (lychee_api), ./lychee/uploads at /app/public/uploads (lychee_worker), ./lychee/logs at /app/storage/logs (lychee_worker), ./lychee/tmp at /app/storage/tmp (lychee_worker), ./lychee/uploads at /data/photos (lychee_facial_recognition). (github.com/LycheeOrg/Lychee/blob/d7deef4/docker-compose.minimal.yaml, checked 2026-10-22)
  • Some services get elevated host access. lychee_api adds Linux capabilities, lychee_worker adds Linux capabilities, lychee_db adds Linux capabilities. (github.com/LycheeOrg/Lychee/blob/d7deef4/docker-compose.minimal.yaml, checked 2026-10-22)

Signals

  • Open issues and pull requests: 18
  • Last push: 2026-10-22 (0 days before this page was built)
  • Latest release: v7.11.1 on 2026-10-13 (9 days before this page was built)