Self-Host PhotoPrism: AI-Powered Photo Management 2026
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
| Feature | PhotoPrism | Immich | Google Photos |
|---|---|---|---|
| License | AGPL 3.0 | AGPL 3.0 | Proprietary |
| Cost | Free | Free | $2.99/mo (100GB) |
| AI Scene detection | Yes (1000+ labels) | Basic | Yes |
| Face recognition | Yes | Yes | Yes |
| Mobile backup app | Limited | Yes (iOS + Android) | Yes |
| Smart albums | Yes | Yes | Yes |
| Video support | Yes | Yes | Yes |
| RAW support | Yes | Yes | Yes |
| Maps / geo | Yes | Yes | Yes |
| Duplicate detection | Yes | Limited | Yes |
| GPU acceleration | Optional (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:
- That path is already mounted as
/photoprism/originals - Go to Library → Index → Start
- PhotoPrism indexes all photos, generates thumbnails, runs AI classification
Import via Web Upload
- Library → Upload — drag and drop from browser
- 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,architecturefood,drink,restaurantbirthday,wedding,graduationdog,cat,bird,horsecar,bicycle,airplane
Browse by label: Search → Labels → [label name]
Face Recognition
PhotoPrism clusters faces and lets you assign names:
- People → Unmatched Faces — review auto-detected faces
- Click a face cluster → Name → type a person's name
- PhotoPrism learns and matches that person across the library
- 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.
Part 5: 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
- Click any photo → share icon
- Generate a public link with optional password + expiry
- 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:
- Progressive Web App (PWA): Visit the URL in Safari/Chrome → "Add to Home Screen" → works like a native app
- Syncthing: Auto-sync phone photos to your server's originals folder → PhotoPrism indexes automatically
- 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.