selfhostedworld.com logoselfhostedworld.com

Try describing what you need:

PhotoPrism logo

PhotoPrism

★ 40.3k

A self-hosted alternative to Lightroom, Google Photos

GNU Affero General Public License v3.0Open Core — Some Features Paid13 Containers — 13 containers — heavy hosting footprintDocker · Compose · Binary +1Needs MariaDB5+ active committers (12mo)

About

PhotoPrism is a self-hosted photo and video management application designed for people who want a private way to browse, organize, search, and share their media. The README presents it as an...

Community Ratings

No ratings yet

Features

  • browse photos and videos
  • RAW and video format support
  • PWA home-screen install
  • advanced search filters
  • automatic labeling
  • face recognition
  • live photos
  • world maps and geocoding
  • metadata extraction and merging
  • WebDAV access

Details

Last Updated
Oct 11, 2026
Created
Jan 27, 2018
Install Methods
dockerdocker-composebinarysource
Requirements
Web browserDocker
Backup & Export
native-exportwebdav-syncfile-backup
Runtime / Stack
GoDocker
Privacy & Independence
Cloud: Optional
Deployment

Deployment: Docker Compose ✓13 Containers — heavy hosting footprintRuns as 13 containers

Run PhotoPrism with Docker Compose

Compose file from the PhotoPrism project repository.

13 services · database: mariadb · infra: traefik, mariadb, postgres · 6 unclassified (qdrant, ollama, open-webui, glauth-plugins, keycloak, prometheus)

no resource limits declared

needs persistent storage (14 volumes)

compose.yaml · @ b7e758c · scanned Jul 7, 2026

## FOR TEST AND DEVELOPMENT ONLY, DO NOT USE IN PRODUCTION   ##
## Setup: https://docs.photoprism.app/developer-guide/setup/ ##

services:
  ## PhotoPrism Build & Test Environment
  photoprism:
    build:
      context: .
      args:
        WORKING_DIR: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}"
    image: photoprism/photoprism:develop
    depends_on:
      - mariadb
      - dummy-webdav
      - dummy-oidc
    stop_grace_period: 15s
    security_opt:
      - seccomp:unconfined
      - apparmor:unconfined
    ## Publish the dev environment's HTTP, TLS, and debugger ports on the host
    ## (bound to 127.0.0.1 by default; override SERVICES_BIND_HOST to change):
    ports:
      - "${SERVICES_BIND_HOST:-127.0.0.1}:2342:2342"   # HTTP (default)
      - "${SERVICES_BIND_HOST:-127.0.0.1}:2443:2443"   # TLS (default)
      - "${SERVICES_BIND_HOST:-127.0.0.1}:2343:2343"   # HTTP (acceptance tests)
      - "${SERVICES_BIND_HOST:-127.0.0.1}:40000:40000" # Go debugger
    shm_size: "2gb"
    ## Set links and labels for use with Traefik reverse proxy
    links:
      - "traefik:localssl.dev"
      - "traefik:app.localssl.dev"
      - "traefik:vision.localssl.dev"
      - "traefik:qdrant.localssl.dev"
      - "traefik:keycloak.localssl.dev"
      - "traefik:dummy-oidc.localssl.dev"
      - "traefik:dummy-webdav.localssl.dev"
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=${COMPOSE_NETWORK_NAME:-photoprism}"
      - "traefik.http.services.photoprism.loadbalancer.server.port=2342"
      - "traefik.http.services.photoprism.loadbalancer.server.scheme=http"
      - "traefik.http.routers.photoprism.entrypoints=websecure"
      - "traefik.http.routers.photoprism.rule=Host(`localssl.dev`) || HostRegexp(`^.+\\.localssl\\.dev`)"
      - "traefik.http.routers.photoprism.priority=2"
      - "traefik.http.routers.photoprism.tls.domains[0].main=localssl.dev"
      - "traefik.http.routers.photoprism.tls.domains[0].sans=*.localssl.dev"
      - "traefik.http.routers.photoprism.tls=true"
    ## Override variables with optional env file, see https://docs.docker.com/reference/compose-file/services/#required
    env_file:
      - path: ".telemetry"
        required: false
      - path: ".env"
        required: false
    ## Configure development environment
    environment:
      ## Run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, and 900-1200):
      PHOTOPRISM_UID: ${UID:-1000}                         # user id, should match your host user id
      PHOTOPRISM_GID: ${GID:-1000}                         # group id
      ## Access Management:
      PHOTOPRISM_ADMIN_USER: "admin"                       # admin login username
      PHOTOPRISM_ADMIN_PASSWORD: "photoprism"              # initial admin password (8-72 characters)
      PHOTOPRISM_AUTH_MODE: "password"                     # authentication mode (public, password)
      PHOTOPRISM_REGISTER_URI: "https://keycloak.localssl.dev/admin/"
      PHOTOPRISM_PASSWORD_RESET_URI: "https://keycloak.localssl.dev/realms/master/login-actions/reset-credentials"
      PHOTOPRISM_USAGE_INFO: "true"
      PHOTOPRISM_FILES_QUOTA: "100"
      ## Customization:
      PHOTOPRISM_DEFAULT_LOCALE: "en"                      # default user interface language, e.g. "en" or "de"
      PHOTOPRISM_PLACES_LOCALE: "local"                    # location details language, e.g. "local", "en", or "de"
      ## OpenID Connect (pre-configured for local tests):
      ## see https://keycloak.localssl.dev/realms/master/.well-known/openid-configuration
      ## the seeded "photoprism-develop" client in scripts/sql/mariadb-init.sql
      ## allows callbacks for /api/v1/oidc/redirect and /i/* paths
      PHOTOPRISM_OIDC_URI: "https://keycloak.localssl.dev/realms/master"
      PHOTOPRISM_OIDC_CLIENT: "photoprism-develop"
      PHOTOPRISM_OIDC_SECRET: "9d8351a0-ca01-4556-9c37-85eb634869b9"
      PHOTOPRISM_OIDC_PROMPT: ""                            # authorization prompt: login, select_account, or consent (empty = seamless SSO)
      PHOTOPRISM_OIDC_PROVIDER: "Keycloak"
      PHOTOPRISM_OIDC_REGISTER: "true"
      PHOTOPRISM_OIDC_WEBDAV: "true"
      PHOTOPRISM_DISABLE_OIDC: "false"
      ## LDAP Authentication (pre-configured for local tests):
      PHOTOPRISM_LDAP_URI: "ldap://dummy-ldap:389"
      PHOTOPRISM_LDAP_INSECURE: "true"
      PHOTOPRISM_LDAP_SYNC: "true"
      PHOTOPRISM_LDAP_BIND: "simple"
      PHOTOPRISM_LDAP_BIND_DN: "cn"
      PHOTOPRISM_LDAP_BASE_DN: "dc=localssl,dc=dev"
      PHOTOPRISM_LDAP_ROLE: ""
      PHOTOPRISM_LDAP_ROLE_DN: "ou=photoprism-*,ou=groups,dc=localssl,dc=dev"
      PHOTOPRISM_LDAP_WEBDAV_DN: "ou=photoprism-webdav,ou=groups,dc=localssl,dc=dev"
      ## HTTPS/TLS Options:
      ## see https://docs.photoprism.app/getting-started/using-https/
      PHOTOPRISM_DISABLE_TLS: "true"
      PHOTOPRISM_DEFAULT_TLS: "true"
      ## Site Information:
      PHOTOPRISM_SITE_URL: "${PHOTOPRISM_SITE_URL:-https://app.localssl.dev/}" # "http(s)://domain.name(:port)/(path)"
      PHOTOPRISM_SITE_NAME: "" # short name for identifying this instance within a cluster
      PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App" # short caption or tagline shown alongside the title
      PHOTOPRISM_SITE_DESCRIPTION: "Tags and finds pictures without getting in your way!" # longer description shown in SEO and social meta tags
      PHOTOPRISM_SITE_AUTHOR: "@photoprism_app" # site owner shown in the author meta tag
      PHOTOPRISM_DEBUG: "true"
      PHOTOPRISM_READONLY: "false"
      PHOTOPRISM_EXPERIMENTAL: "true"
      PHOTOPRISM_HTTP_MODE: "debug"
      PHOTOPRISM_HTTP_HOST: "0.0.0.0"
      PHOTOPRISM_HTTP_PORT: 2342
      PHOTOPRISM_HTTP_COMPRESSION: "zstd,gzip"       # improves transfer speed and bandwidth utilization (none, gzip, zstd, or comma-separated list e.g. "zstd,gzip")
      MYSQL_TCP_PORT: "${MARIADB_PORT:-4001}"        # default MariaDB database port used by the "mariadb" client (see .my.cnf)
      PGPORT: "${POSTGRES_PORT:-4002}"               # default PostgreSQL database port used by "psql"
      PHOTOPRISM_DATABASE_DRIVER: "mysql"
      PHOTOPRISM_DATABASE_SERVER: "mariadb:${MARIADB_PORT:-4001}"
      PHOTOPRISM_DATABASE_NAME: "photoprism"
      PHOTOPRISM_DATABASE_USER: "root"
      PHOTOPRISM_DATABASE_PASSWORD: "photoprism"
      PHOTOPRISM_TEST_DRIVER: "sqlite"
      # PHOTOPRISM_TEST_DSN_MYSQL8: "root:photoprism@tcp(mysql:${MARIADB_PORT:-4001})/photoprism?charset=utf8mb4,utf8&collation=utf8mb4_unicode_ci&parseTime=true&timeout=15s"
      WORKING_DIR: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}"
      PHOTOPRISM_ASSETS_PATH: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}/assets"
      PHOTOPRISM_STORAGE_PATH: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}/storage"
      PHOTOPRISM_ORIGINALS_PATH: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}/storage/originals"
      PHOTOPRISM_ORIGINALS_LIMIT: 128000             # sets originals file size limit to 128 GB
      PHOTOPRISM_IMPORT_PATH: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}/storage/import"
      PHOTOPRISM_DISABLE_CHOWN: "false"              # disables updating storage permissions via chmod and chown on startup
      PHOTOPRISM_DISABLE_BACKUPS: "false"            # disables backing up albums and photo metadata to YAML files
      PHOTOPRISM_DISABLE_WEBDAV: "false"             # disables built-in WebDAV server
      PHOTOPRISM_DISABLE_MCP: "false"                # disables Model Context Protocol (MCP) API endpoint for AI agent integrations
      PHOTOPRISM_DISABLE_SETTINGS: "false"           # disables settings UI and API
      PHOTOPRISM_DISABLE_PLACES: "false"             # disables reverse geocoding and maps
      PHOTOPRISM_DISABLE_EXIFTOOL: "false"           # disables creating JSON metadata sidecar files with ExifTool
      PHOTOPRISM_DISABLE_TENSORFLOW: "false"         # disables all features depending on TensorFlow
      PHOTOPRISM_DISABLE_RAW: "false"                # disables indexing and conversion of RAW images
      PHOTOPRISM_RAW_PRESETS: "false"                # enables applying user presets when converting RAW images (reduces performance)
      PHOTOPRISM_DETECT_NSFW: "false"                # automatically flags photos as private that MAY be offensive (requires TensorFlow)
      PHOTOPRISM_UPLOAD_NSFW: "false"                # allows uploads that MAY be offensive (no effect without TensorFlow)
      PHOTOPRISM_UPLOAD_ALLOW: ""                    # restricts uploads to these file types (comma-separated list of EXTENSIONS; leave blank to allow all)
      PHOTOPRISM_UPLOAD_ARCHIVES: "true"             # allows upload of zip archives (will be extracted before import)
      PHOTOPRISM_THUMB_LIBRARY: "auto"               # image processing library to be used for generating thumbnails (auto, vips)
      PHOTOPRISM_THUMB_UNCACHED: "true"              # enables on-demand thumbnail rendering (high memory and cpu usage)
      ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/):
      # PHOTOPRISM_FFMPEG_ENCODER: "software"        # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, vaapi, or vulkan)
      # LIBVA_DRIVER_NAME: "i965"                    # For Intel architectures Haswell and older which do not support QSV yet but use VAAPI instead
      PHOTOPRISM_FFMPEG_SIZE: "1920"                 # video size limit in pixels (720-7680) (default: 3840)
      # PHOTOPRISM_FFMPEG_BITRATE: "64"              # video bitrate limit in Mbps (default: 60)
      ## Run/install on first startup (options: update tensorflow https intel gpu davfs yt-dlp):
      PHOTOPRISM_INIT: "https"
      ## Computer Vision (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
      PHOTOPRISM_VISION_API: "true"                  # server: enables service API endpoints under /api/v1/vision (requires access token)
      PHOTOPRISM_VISION_URI: ""                      # client: service URI, e.g. http://hostname/api/v1/vision (leave blank to disable)
      PHOTOPRISM_VISION_KEY: ""                      # client: service access token (for authentication)
      OLLAMA_BASE_URL: "http://ollama:11434"         # use "https://ollama.com" for Ollama Cloud
      OLLAMA_API_KEY: ""                             # API key required to access Ollama (optional)
      ## Additional dependencies and tools:
      TF_CPP_MIN_LOG_LEVEL: 1
      GOCACHE: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}/.local/gocache"
      CODEX_HOME: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}/.local/codex"
      CLAUDE_CONFIG_DIR: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}/.local/claude"
      GH_REPO: "photoprism/photoprism"
      GH_CONFIG_DIR: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}/.local/gh"
    ## Shared devices for video hardware transcoding (optional):
    # devices:
    #  - "/dev/dri:/dev/dri"                         # Required Intel QSV or VAAPI hardware transcoding
    #  - "/dev/video11:/dev/video11"                 # Video4Linux Video Encode Device (h264_v4l2m2m)
    working_dir: "${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}"
    volumes:
      - ".:${WORKING_DIR:-/go/src/github.com/photoprism/photoprism}"
      - "./storage:/photoprism"
      - "go-mod:/go/pkg/mod"

  ## Traefik v3 (Reverse Proxy)
  ## includes "*.localssl.dev" SSL certificate for test environments
  ## Docs: https://doc.traefik.io/traefik/
  traefik:
    image: photoprism/traefik:latest
    stop_grace_period: 15s
    security_opt:
      - no-new-privileges:true
    command:
      - --api.dashboard=false
      - --global.checknewversion=false
      - --global.sendanonymoususage=false
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
    ## Publish Traefik's HTTP and HTTPS entrypoints on the host (bound to every interface
    ## by default; override TRAEFIK_BIND_HOST, TRAEFIK_HTTP_PORT, or TRAEFIK_HTTPS_PORT):
    ports:
      - "${TRAEFIK_BIND_HOST:-0.0.0.0}:${TRAEFIK_HTTP_PORT:-80}:80"    # HTTP (redirects to HTTPS)
      - "${TRAEFIK_BIND_HOST:-0.0.0.0}:${TRAEFIK_HTTPS_PORT:-443}:443" # HTTPS (required)
    labels:
      - "traefik.enable=true"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock" # enables Traefik to watch services

  ## MariaDB (Database Server)
  ## Docs: https://mariadb.com/docs/reference/
  ## Release Notes: https://mariadb.com/kb/en/changes-improvements-in-mariadb-1011/
  mariadb:
    image: mariadb:12.3
    stop_grace_period: 15s
    security_opt: # see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239
      - seccomp:unconfined
      - apparmor:unconfined
    command:
      - "--port=${MARIADB_PORT:-4001}"
      - --innodb-strict-mode=1
      - --innodb-buffer-pool-size=256M
      - --transaction-isolation=READ-COMMITTED
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --max-connections=512
      - --innodb-rollback-on-timeout=OFF
      - --innodb-lock-wait-timeout=120
    expose:
      - "${MARIADB_PORT:-4001}" # does not default to 3306 in order to avoid conflicts and confuse port scanners
    ## Publish the MariaDB port on the host for direct access from the host:
    ports:
      - "${SERVICES_BIND_HOST:-127.0.0.1}:${MARIADB_PORT:-4001}:${MARIADB_PORT:-4001}" # MariaDB
    volumes:
      - "mariadb:/var/lib/mysql"
      - "./scripts/sql/mariadb-init.sql:/docker-entrypoint-initdb.d/init.sql"
    environment:
      MARIADB_AUTO_UPGRADE: "1"
      MARIADB_INITDB_SKIP_TZINFO: "1"
      MARIADB_DATABASE: "photoprism"
      MARIADB_USER: "photoprism"
      MARIADB_PASSWORD: "photoprism"
      MARIADB_ROOT_PASSWORD: "photoprism"

  ## PostgreSQL (Database Server)
  ## Docs: https://www.postgresql.org/docs/current/index.html
  ## Release Notes: https://www.postgresql.org/docs/release/
  postgres:
    image: postgres:18-alpine
    ## Only starts if the "all" or "postgres" profile is specified:
    ## docker compose --profile postgres up -d
    profiles: [ "all", "postgres" ]
    stop_grace_period: 15s
    expose:
      - "${POSTGRES_PORT:-4002}"
    ## Publish the PostgreSQL port on the host for direct access from the host:
    ports:
      - "${SERVICES_BIND_HOST:-127.0.0.1}:${POSTGRES_PORT:-4002}:${POSTGRES_PORT:-4002}" # PostgreSQL
    volumes:
      - "postgres:/var/lib/postgresql"
      - "./scripts/sql/postgresql-init.sql:/docker-entrypoint-initdb.d/init.sql"
    environment:
      POSTGRES_DB: photoprism
      POSTGRES_USER: photoprism
      POSTGRES_PASSWORD: photoprism
      PGPORT: "${POSTGRES_PORT:-4002}"
      PGDATA: /var/lib/postgresql/18/docker

  ## Qdrant (Vector Database)
  ## Docs: https://qdrant.tech/documentation/guides/installation/#docker-compose
  ## Release Notes: https://github.com/qdrant/qdrant/releases
  ## Web UI: https://qdrant.localssl.dev/dashboard
  qdrant:
    image: qdrant/qdrant:latest
    ## Only starts if the "all" or "qdrant" profile is specified:
    ## docker compose --profile qdrant up -d
    profiles: [ "all", "qdrant" ]
    stop_grace_period: 10s
    links:
      - "traefik:localssl.dev"
      - "traefik:app.localssl.dev"
      - "traefik:vision.localssl.dev"
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=${COMPOSE_NETWORK_NAME:-photoprism}"
      - "traefik.http.services.qdrant.loadbalancer.server.port=6333"
      - "traefik.http.services.qdrant.loadbalancer.server.scheme=http"
      - "traefik.http.routers.qdrant.entrypoints=websecure"
      - "traefik.http.routers.qdrant.rule=Host(`qdrant.localssl.dev`)"
      - "traefik.http.routers.qdrant.priority=3"
      - "traefik.http.routers.qdrant.tls.domains[0].main=localssl.dev"
      - "traefik.http.routers.qdrant.tls.domains[0].sans=*.localssl.dev"
      - "traefik.http.routers.qdrant.tls=true"
    expose:
      - 6333
      - 6334
      - 6335
    volumes:
      - "./.qdrant.yaml:/qdrant/config/production.yaml"
      - "./storage/services/qdrant:/qdrant/storage"

  ## Ollama Large-Language Model Runner
  ## run "ollama pull [name]:[version]" to download a vision model
  ## listed at <https://ollama.com/search?c=vision>, for example:
  ## docker compose exec ollama ollama pull gemma3:latest
  ollama:
    image: ollama/ollama:latest
    ## Only starts if the "all", "ollama", or "vision" profile is specified:
    ## docker compose --profile ollama up -d
    profiles: [ "all", "ollama", "vision" ]
    stop_grace_period: 10s
    ## Optionally publish the Ollama API on the host (no built-in auth — private/internal networks only):
    # ports:
    #   - "${SERVICES_BIND_HOST:-127.0.0.1}:11434:11434" # Ollama API
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=${COMPOSE_NETWORK_NAME:-photoprism}"
      - "traefik.http.services.ollama.loadbalancer.server.port=11434"
      - "traefik.http.routers.ollama.rule=Host(`ollama.localssl.dev`)"
      - "traefik.http.routers.ollama.entrypoints=websecure"
      - "traefik.http.routers.ollama.tls=true"
    environment:
      ## Ollama Configuration Options:
      OLLAMA_HOST: "0.0.0.0:11434"
      OLLAMA_MODELS: "/root/.ollama"   # model storage path (see volumes section below)
      OLLAMA_MAX_QUEUE: "100"          # maximum number of queued requests
      OLLAMA_NUM_PARALLEL: "1"         # maximum number of parallel requests
      OLLAMA_MAX_LOADED_MODELS: "1"    # maximum number of loaded models per GPU
      OLLAMA_LOAD_TIMEOUT: "5m"        # maximum time for loading models (default "5m")
      OLLAMA_KEEP_ALIVE: "5m"          # duration that models stay loaded in memory (default "5m")
      OLLAMA_CONTEXT_LENGTH: "4096"    # maximum input context length
      OLLAMA_MULTIUSER_CACHE: "false"  # optimize prompt caching for multi-user scenarios
      OLLAMA_NOPRUNE: "false"          # disables pruning of model blobs at startup
      OLLAMA_NOHISTORY: "true"         # disables readline history
      OLLAMA_FLASH_ATTENTION: "true"   # required for OLLAMA_KV_CACHE_TYPE quantization
      OLLAMA_KV_CACHE_TYPE: "f16"      # cache precision: f16 (default), q8_0, q4_0
      OLLAMA_SCHED_SPREAD: "false"     # allows scheduling models across all GPUs.
      # OLLAMA_DEBUG: "true"             # shows additional debug information
      # OLLAMA_INTEL_GPU: "true"         # enables experimental Intel GPU detection
      ## Telemetry / privacy opt-outs (containers do not inherit /etc/environment):
      DO_NOT_TRACK: "true"
      HF_HUB_DISABLE_TELEMETRY: "1"
      # OLLAMA_NO_CLOUD: "1"           # uncomment to disable Ollama Cloud models/features
      ## NVIDIA GPU Hardware Acceleration (optional):
      # NVIDIA_VISIBLE_DEVICES: "all"
      # NVIDIA_DRIVER_CAPABILITIES: "compute,utility"
    volumes:
      - "./storage/services/ollama:/root/.ollama"
    ## NVIDIA GPU Hardware Acceleration (optional):
    # deploy:
    #  resources:
    #    reservations:
    #      devices:
    #        - driver: "nvidia"
    #          capabilities: [ gpu ]
    #          count: "all"

  ## Open WebUI, a Web Interface for Ollama
  ## see https://github.com/open-webui/open-webui
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    ## Only starts if the "all", "ollama", "open-webui", or "vision" profile is specified:
    ## docker compose --profile open-webui up -d
    profiles: [ "all", "ollama", "open-webui", "vision" ]
    stop_grace_period: 10s
    ## Publish Open WebUI on the host (default access is via https://chat.localssl.dev/ through Traefik):
    ports:
      - "${SERVICES_BIND_HOST:-127.0.0.1}:8080:8080" # Open WebUI
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=${COMPOSE_NETWORK_NAME:-photoprism}"
      - "traefik.http.services.open-webui.loadbalancer.server.port=8080"
      - "traefik.http.routers.open-webui.rule=Host(`chat.localssl.dev`) || Host(`open-webui.localssl.dev`) || Host(`ollama-ui.localssl.dev`)"
      - "traefik.http.routers.open-webui.entrypoints=websecure"
      - "traefik.http.routers.open-webui.tls=true"
    environment:
      WEBUI_URL: "https://chat.localssl.dev"
      # WEBUI_SECRET_KEY: ""
      OLLAMA_BASE_URL: "http://ollama:11434"
      ANONYMIZED_TELEMETRY: "false"   # disable Chroma telemetry
      HF_HUB_DISABLE_TELEMETRY: "1"   # disable Hugging Face telemetry
      # HUGGING_FACE_HUB_TOKEN: ""      # see https://huggingface.co/docs/hub/en/security-tokens
    volumes:
      - "./storage/services/open-webui:/app/backend/data"

  ## PhotoPrism® Computer Vision API
  ## see https://github.com/photoprism/photoprism-vision
  photoprism-vision:
    image: photoprism/vision:latest
    ## Only starts if the "all" or "vision" profile is specified:
    ## docker compose --profile vision up -d
    profiles: [ "all", "vision" ]
    stop_grace_period: 15s
    working_dir: "/app"
    links:
      - "traefik:localssl.dev"
      - "traefik:app.localssl.dev"
      - "traefik:qdrant.localssl.dev"
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=${COMPOSE_NETWORK_NAME:-photoprism}"
      - "traefik.http.services.vision.loadbalancer.server.port=5000"
      - "traefik.http.services.vision.loadbalancer.server.scheme=http"
      - "traefik.http.routers.vision.entrypoints=websecure"
      - "traefik.http.routers.vision.rule=Host(`vision.localssl.dev`)"
      - "traefik.http.routers.vision.priority=3"
      - "traefik.http.routers.vision.tls.domains[0].main=localssl.dev"
      - "traefik.http.routers.vision.tls.domains[0].sans=*.localssl.dev"
      - "traefik.http.routers.vision.tls=true"
    expose:
      - 5000
    environment:
      TF_CPP_MIN_LOG_LEVEL: 2
      ## Ollama client configuration (for the service, see below):
      OLLAMA_ENABLED: "true"
      OLLAMA_HOST: "http://ollama:11434"

  ## Dummy WebDAV Server
  dummy-webdav:
    image: photoprism/dummy-webdav:251210
    stop_grace_period: 10s
    environment:
      WEBDAV_USERNAME: admin
      WEBDAV_PASSWORD: photoprism
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=${COMPOSE_NETWORK_NAME:-photoprism}"
      - "traefik.http.services.dummy-webdav.loadbalancer.server.port=80"
      - "traefik.http.routers.dummy-webdav.entrypoints=websecure"
      - "traefik.http.routers.dummy-webdav.rule=Host(`dummy-webdav.localssl.dev`)"
      - "traefik.http.routers.dummy-webdav.priority=3"
      - "traefik.http.routers.dummy-webdav.tls.domains[0].main=localssl.dev"
      - "traefik.http.routers.dummy-webdav.tls.domains[0].sans=*.localssl.dev"
      - "traefik.http.routers.dummy-webdav.tls=true"

  ## Dummy OIDC Identity Provider
  dummy-oidc:
    image: photoprism/dummy-oidc:260427
    stop_grace_period: 5s
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=${COMPOSE_NETWORK_NAME:-photoprism}"
      - "traefik.http.services.dummy-oidc.loadbalancer.server.port=9998"
      - "traefik.http.routers.dummy-oidc.entrypoints=websecure"
      - "traefik.http.routers.dummy-oidc.rule=Host(`dummy-oidc.localssl.dev`)"
      - "traefik.http.routers.dummy-oidc.priority=3"
      - "traefik.http.routers.dummy-oidc.tls.domains[0].main=localssl.dev"
      - "traefik.http.routers.dummy-oidc.tls.domains[0].sans=*.localssl.dev"
      - "traefik.http.routers.dummy-oidc.tls=true"

  ## Dummy LDAP Directory Server
  ## Docs: https://glauth.github.io/docs/
  dummy-ldap:
    image: glauth/glauth-plugins:latest
    stop_grace_period: 5s
    ## Publish the dummy-ldap port on the host (default access is via "dummy-ldap:389" within the compose network):
    ports:
      - "${SERVICES_BIND_HOST:-127.0.0.1}:389:389" # LDAP
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=${COMPOSE_NETWORK_NAME:-photoprism}"
      - "traefik.http.services.dummy-ldap.loadbalancer.server.port=5555"
      - "traefik.http.routers.dummy-ldap.entrypoints=websecure"
      - "traefik.http.routers.dummy-ldap.rule=Host(`dummy-ldap.localssl.dev`)"
      - "traefik.http.routers.dummy-ldap.priority=3"
      - "traefik.http.routers.dummy-ldap.tls.domains[0].main=localssl.dev"
      - "traefik.http.routers.dummy-ldap.tls.domains[0].sans=*.localssl.dev"
      - "traefik.http.routers.dummy-ldap.tls=true"
    volumes:
      - "./.ldap.cfg:/app/config/config.cfg"

  ## Keycloak (OIDC Identity Provider)
  ## Docs: https://www.keycloak.org/docs/latest/server_admin/
  ## Login with "user / photoprism" and "admin / photoprism".
  keycloak:
    image: quay.io/keycloak/keycloak:25.0
    ## Only starts if the "all", "auth", or "keycloak" profile is specified:
    ## docker compose --profile keycloak up -d
    profiles: [ "all", "auth", "keycloak" ]
    stop_grace_period: 10s
    command: "start-dev" # development mode, do not use this in production!
    links:
      - "traefik:localssl.dev"
      - "traefik:app.localssl.dev"
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=${COMPOSE_NETWORK_NAME:-photoprism}"
      - "traefik.http.services.keycloak.loadbalancer.server.port=8080"
      - "traefik.http.routers.keycloak.entrypoints=websecure"
      - "traefik.http.routers.keycloak.rule=Host(`keycloak.localssl.dev`)"
      - "traefik.http.routers.keycloak.priority=3"
      - "traefik.http.routers.keycloak.tls.domains[0].main=localssl.dev"
      - "traefik.http.routers.keycloak.tls.domains[0].sans=*.localssl.dev"
      - "traefik.http.routers.keycloak.tls=true"
    environment: # see https://www.keycloak.org/server/all-config
      KEYCLOAK_ADMIN: "admin"
      KEYCLOAK_ADMIN_PASSWORD: "photoprism"
      KC_METRICS_ENABLED: "false"
      KC_HOSTNAME: "keycloak.localssl.dev"
      KC_HOSTNAME_STRICT: "false"
      KC_PROXY: "edge"
      KC_DB: "mariadb"
      KC_DB_URL: "jdbc:mariadb://mariadb:${MARIADB_PORT:-4001}/keycloak"
      KC_DB_USERNAME: "keycloak"
      KC_DB_PASSWORD: "keycloak"

  ## Run "docker compose --profile prometheus up" to start your development environment with Prometheus.
  ## Docs: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#oauth2
  ## The following grants API access to Prometheus with the preconfigured client credentials (adjust flags as needed):
  ## ./photoprism client add --id=cs5cpu17n6gj2qo5 --secret=xcCbOrw6I0vcoXzhnOmXhjpVSyFq0l0e -s metrics -n Prometheus -e 60 -t 1
  prometheus:
    image: prom/prometheus:latest
    ## Only starts if the "all", "auth", or "prometheus" profile is specified:
    ## docker compose --profile prometheus up -d
    profiles: [ "all", "auth", "prometheus" ]
    stop_grace_period: 10s
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=${COMPOSE_NETWORK_NAME:-photoprism}"
      - "traefik.http.services.prometheus.loadbalancer.server.port=9090"
      - "traefik.http.routers.prometheus.entrypoints=websecure"
      - "traefik.http.routers.prometheus.rule=Host(`prometheus.localssl.dev`)"
      - "traefik.http.routers.prometheus.priority=3"
      - "traefik.http.routers.prometheus.tls.domains[0].main=localssl.dev"
      - "traefik.http.routers.prometheus.tls.domains[0].sans=*.localssl.dev"
      - "traefik.http.routers.prometheus.tls=true"
    volumes:
      - "./prometheus.yml:/etc/prometheus/prometheus.yml"

## Create named volume for Go module cache
volumes:
  go-mod:
    driver: local
  mariadb:
    driver: local
  postgres:
    driver: local

## Create a shared bridge network for connecting with services in other compose.yaml files.
## Override COMPOSE_NETWORK_NAME to run multiple parallel environments without colliding on the default name.
networks:
  default:
    name: ${COMPOSE_NETWORK_NAME:-photoprism}
    driver: bridge

This file may be out of date. Check the PhotoPrism documentation for current setup steps.

Open-source health

FOSS with issues

copyleft-claopen-core

Source: isitreallyfoss.com · reviewed Sep 2025

Track your self-hosted stack

Bookmark software to try, rate tools you've used, and keep your collection in one place.

Metadata extracted from README on Jul 26, 2026

Related Software

Damselfly logo

Damselfly

1.8k
Damselfly is a server-based digital photo management system for managing large folder-based photo collections with fast search, tagging, and AI-assisted recognition.
Photo GalleriesMedia ManagementArtificial Intelligence+4
Details
Nextcloud Memories logo

Nextcloud Memories

3.9k
Memories is a photo management app for Nextcloud with timeline browsing, sharing, metadata editing, and AI-based tagging.
Photo GalleriesCopyleft LicenseMedia Management+3
5 ContainersCloud Required
Details
ChronoFrame logo

ChronoFrame

1.9k
A self-hosted web application for photo display and management with map-based browsing, EXIF parsing, and multi-format image support.
Photo GalleriesPermissive LicenseMedia Management+3
Details
Mejiro logo

Mejiro

184
Pellicola is a PHP web app for instant photo publishing and browsing.
Photo GalleriesMedia ManagementWeb Servers+3
Details
LibrePhotos logo

LibrePhotos

8.1k
LibrePhotos is a self-hosted photo management service with face recognition, object detection, and semantic search.
Photo GalleriesMedia ManagementArtificial Intelligence+2
Details
HomeGallery logo

HomeGallery

1.2k
HomeGallery is a self-hosted open-source web gallery for browsing personal photos and videos with tagging, mobile-friendly viewing, and AI-powered image and face discovery.
Photo GalleriesPermissive LicenseMedia Management+2
Details
pixlstash logo

pixlstash

89
PixlStash is a local picture library server for organizing, filtering, and reviewing large image collections.
Media ManagementPhoto GalleriesArtificial Intelligence+2
Single Container
Details
Thumbor logo

Thumbor

10.5k
thumbor is a smart imaging service for on-demand cropping, resizing, transforming, and optimizing images.
Photo GalleriesMedia ManagementWeb Servers+2
Details
Immich logo

Immich

116.0k
High performance self-hosted photo and video management solution.
Photo GalleriesCopyleft LicenseMedia Management+1
6 Containers
Details
PiGallery 2 logo

PiGallery 2

2.3k
A fast directory-first photo gallery website for low-resource servers, especially Raspberry Pi.
Photo GalleriesMedia ManagementSelf Hosting Solutions+1
2 Containers
Details
Piwigo logo

Piwigo

3.9k
Piwigo is an open source web-based photo gallery for managing and sharing photo libraries.
Photo GalleriesMedia ManagementCMS Platforms+3
Fully FOSS
Details
Photoview logo

Photoview

6.5k
Photoview is a self-hosted photo gallery that scans local directories, generates thumbnails, and organizes photos and videos for fast browsing.
Photo GalleriesMedia ManagementSelf Hosting Solutions+2
4 ContainersFully FOSS
Details