Skip to main content

Self-Host PhotoPrism: AI-Powered Photo Management 2026

·OSSAlt Team
photoprismphotosaiself-hostingdocker2026

TL;DR

PhotoPrism (AGPL 3.0, ~34K GitHub stars, Go) is a self-hosted AI photo management app that automatically classifies, tags, and organizes your photo library. It detects scenes ("beach", "mountains", "food"), recognizes faces, geo-tags from EXIF, and creates smart albums — all running locally without sending data to any cloud. Compare: Google Photos charges $2.99/month for 100GB and trains on your photos. PhotoPrism is free, your data stays on-prem, and CPU inference runs on any server.

Key Takeaways

  • PhotoPrism: AGPL 3.0, ~34K stars, Go — AI classification, face recognition, smart albums
  • Scene detection: Classifies 1000+ scene types (beach, food, wedding, architecture) automatically
  • Face recognition: Clusters faces, lets you name people — works fully offline
  • No cloud required: All AI inference runs locally; optional GPU acceleration via TensorFlow
  • Import-friendly: Reads from any directory, preserves folder structure, handles duplicates
  • Immich comparison: PhotoPrism = mature AI + albums; Immich = faster UI + mobile backup focus

PhotoPrism vs Immich vs Google Photos

FeaturePhotoPrismImmichGoogle Photos
LicenseAGPL 3.0AGPL 3.0Proprietary
CostFreeFree$2.99/mo (100GB)
AI Scene detectionYes (1000+ labels)BasicYes
Face recognitionYesYesYes
Mobile backup appLimitedYes (iOS + Android)Yes
Smart albumsYesYesYes
Video supportYesYesYes
RAW supportYesYesYes
Maps / geoYesYesYes
Duplicate detectionYesLimitedYes
GPU accelerationOptional (TF)Yes (ML)
GitHub Stars~34K~55K

Part 1: Docker Setup

# docker-compose.yml
services:
  photoprism:
    image: photoprism/photoprism:latest
    container_name: photoprism
    restart: unless-stopped
    ports:
      - "2342:2342"
    security_opt:
      - seccomp:unconfined
      - apparmor:unconfined
    environment:
      PHOTOPRISM_AUTH_MODE: "password"
      PHOTOPRISM_SITE_URL: "https://photos.yourdomain.com"
      PHOTOPRISM_ORIGINALS_LIMIT: 5000          # MB, -1 = unlimited
      PHOTOPRISM_HTTP_COMPRESSION: "gzip"
      PHOTOPRISM_LOG_LEVEL: "info"
      PHOTOPRISM_READONLY: "false"
      PHOTOPRISM_EXPERIMENTAL: "false"
      PHOTOPRISM_DISABLE_CHOWN: "false"
      PHOTOPRISM_DISABLE_WEBDAV: "false"
      PHOTOPRISM_DISABLE_SETTINGS: "false"
      PHOTOPRISM_DISABLE_TENSORFLOW: "false"    # Set true to skip AI (faster startup)
      PHOTOPRISM_DISABLE_FACES: "false"
      PHOTOPRISM_DISABLE_CLASSIFICATION: "false"
      PHOTOPRISM_DISABLE_VECTORS: "false"
      PHOTOPRISM_DISABLE_RAW: "false"
      PHOTOPRISM_RAW_PRESETS: "false"
      PHOTOPRISM_JPEG_QUALITY: 85
      PHOTOPRISM_DETECT_NSFW: "false"
      PHOTOPRISM_UPLOAD_NSFW: "true"
      PHOTOPRISM_DATABASE_DRIVER: "mysql"
      PHOTOPRISM_DATABASE_SERVER: "mariadb:3306"
      PHOTOPRISM_DATABASE_NAME: "photoprism"
      PHOTOPRISM_DATABASE_USER: "photoprism"
      PHOTOPRISM_DATABASE_PASSWORD: "${PHOTOPRISM_DB_PASSWORD}"
      PHOTOPRISM_ADMIN_USER: "admin"
      PHOTOPRISM_ADMIN_PASSWORD: "${PHOTOPRISM_ADMIN_PASSWORD}"
      PHOTOPRISM_UID: 1000
      PHOTOPRISM_GID: 1000
    volumes:
      - /path/to/your/photos:/photoprism/originals   # Your photo library (read-write)
      - photoprism_storage:/photoprism/storage        # AI models, thumbnails, sidecar files
    depends_on:
      mariadb:
        condition: service_healthy

  mariadb:
    image: mariadb:11
    container_name: photoprism_db
    restart: unless-stopped
    command: --innodb-buffer-pool-size=512M --transaction-isolation=READ-COMMITTED
    healthcheck:
      test: mysqladmin ping -h localhost -u photoprism --password="${PHOTOPRISM_DB_PASSWORD}"
      interval: 10s
      start_period: 20s
    environment:
      MARIADB_AUTO_UPGRADE: "1"
      MARIADB_INITDB_SKIP_TZINFO: "1"
      MARIADB_DATABASE: "photoprism"
      MARIADB_USER: "photoprism"
      MARIADB_PASSWORD: "${PHOTOPRISM_DB_PASSWORD}"
      MARIADB_ROOT_PASSWORD: "${MARIADB_ROOT_PASSWORD}"
    volumes:
      - mariadb_data:/var/lib/mysql

volumes:
  photoprism_storage:
  mariadb_data:
# .env
PHOTOPRISM_ADMIN_PASSWORD=your-secure-admin-password
PHOTOPRISM_DB_PASSWORD=your-db-password
MARIADB_ROOT_PASSWORD=your-root-password

docker compose up -d

Visit http://your-server:2342 — login with admin / your password.


Part 2: HTTPS with Caddy

photos.yourdomain.com {
    reverse_proxy localhost:2342
}

Part 3: Initial Photo Import

Import from an Existing Directory

If your photos are already on the server at /path/to/your/photos:

  1. That path is already mounted as /photoprism/originals
  2. Go to Library → Index → Start
  3. PhotoPrism indexes all photos, generates thumbnails, runs AI classification

Import via Web Upload

  1. Library → Upload — drag and drop from browser
  2. Files go into /photoprism/originals/[year]/[month]/

Import via WebDAV

PhotoPrism exposes a WebDAV endpoint for remote file copying:

# Mount with macOS Finder or any WebDAV client:
# URL: https://photos.yourdomain.com/originals/

# Or use rclone:
rclone copy /local/photos "photoprism:originals" --transfers 4

CLI Import

docker exec photoprism photoprism import /photoprism/originals

Part 4: AI Features

Scene Classification

After indexing, PhotoPrism automatically adds labels like:

  • beach, sunset, mountains, forest, city, architecture
  • food, drink, restaurant
  • birthday, wedding, graduation
  • dog, cat, bird, horse
  • car, bicycle, airplane

Browse by label: Search → Labels → [label name]

Face Recognition

PhotoPrism clusters faces and lets you assign names:

  1. People → Unmatched Faces — review auto-detected faces
  2. Click a face cluster → Name → type a person's name
  3. PhotoPrism learns and matches that person across the library
  4. People → [Name] — all photos of that person

Face recognition runs locally via TensorFlow — no data leaves your server.

Smart Albums

PhotoPrism auto-creates albums based on:

  • Moments: Auto-grouped by date + location ("Paris, June 2024")
  • States/Countries: Photos grouped by geo-location
  • Calendar: Browse by year/month
  • Colors: Filter by dominant color

Manual albums: Albums → + New Album → add photos manually or via search.


PhotoPrism's search is powerful:

# Search by label:
label:beach

# Search by date range:
after:2024-01-01 before:2024-12-31

# Search by camera:
camera:iphone

# Search by person:
person:Alice

# Search by geo:
near:"San Francisco"

# Combine:
label:sunset after:2024-06-01 before:2024-09-01

# Only videos:
type:video

# Favorites:
is:favorite

# Unreviewed:
quality:0

Part 6: GPU Acceleration (Optional)

For faster AI classification and video transcoding:

NVIDIA GPU

services:
  photoprism:
    image: photoprism/photoprism:latest
    # Add GPU access:
    runtime: nvidia
    environment:
      NVIDIA_VISIBLE_DEVICES: all
      PHOTOPRISM_INIT: "tensorflow-gpu intel-graphics"

Requires nvidia-container-toolkit on the host.

Apple Silicon / ARM

PhotoPrism has native ARM64 builds — runs well on Apple Silicon Macs or ARM servers (Raspberry Pi 4+ with 4GB RAM for small libraries).

CPU-Only Mode (No AI)

If you want a fast photo browser without AI overhead:

environment:
  PHOTOPRISM_DISABLE_TENSORFLOW: "true"
  PHOTOPRISM_DISABLE_FACES: "true"
  PHOTOPRISM_DISABLE_CLASSIFICATION: "true"

Indexing is 10-20x faster — you lose auto-labeling and face recognition but keep geo, EXIF, and manual organization.


Part 7: Storage Layout

/photoprism/originals/    ← Your original photos (never modified)
  2024/
    01/
      IMG_1234.jpg
      IMG_1235.jpg
    06/
      vacation/
        IMG_2000.jpg

/photoprism/storage/      ← PhotoPrism's working directory
  cache/
    thumbnails/           ← Auto-generated thumbnails
  sidecar/                ← XMP/JSON metadata sidecar files
  albums/                 ← Album definitions
  tensorflow/             ← AI model files (~300MB, auto-downloaded on first run)

Originals are never modified. Metadata is stored in sidecar files and the database.


Part 8: Sharing and Albums

Share a Photo or Album

  1. Click any photo → share icon
  2. Generate a public link with optional password + expiry
  3. Recipients can view without creating an account

WebDAV Access

Access your photos from Lightroom, Finder, or any WebDAV client:

URL: https://photos.yourdomain.com/originals/
Username: admin
Password: your-password

API

# Get session token:
TOKEN=$(curl -s -X POST https://photos.yourdomain.com/api/v1/session \
  -d '{"username":"admin","password":"your-password"}' | jq -r .id)

# Search photos:
curl "https://photos.yourdomain.com/api/v1/photos?q=beach&count=10" \
  -H "X-Session-ID: $TOKEN" | jq '.[].FileName'

# Download a photo:
curl "https://photos.yourdomain.com/api/v1/photo/UUID/dl" \
  -H "X-Session-ID: $TOKEN" -O

Part 9: Mobile Access

PhotoPrism doesn't have a dedicated mobile backup app (unlike Immich). Workarounds:

  1. Progressive Web App (PWA): Visit the URL in Safari/Chrome → "Add to Home Screen" → works like a native app
  2. Syncthing: Auto-sync phone photos to your server's originals folder → PhotoPrism indexes automatically
  3. Rclone: Scheduled sync from phone (via Termux on Android or Scriptable on iOS)

Maintenance

# Update PhotoPrism:
docker compose pull
docker compose up -d

# Reindex after adding photos:
docker exec photoprism photoprism index --cleanup

# Convert RAW to JPEG (for web preview):
docker exec photoprism photoprism convert

# Rebuild faces index:
docker exec photoprism photoprism faces index

# Backup database:
docker exec photoprism_db mysqldump -u photoprism -p"${PHOTOPRISM_DB_PASSWORD}" photoprism \
  | gzip > photoprism-db-$(date +%Y%m%d).sql.gz

# Logs:
docker compose logs -f photoprism

# Check storage size:
docker exec photoprism du -sh /photoprism/storage/cache/thumbnails/

See all open source photo management tools at OSSAlt.com/categories/photos.

Comments