Self-Hosting Photo Management: PhotoPrism on Debian Linux (Go Backend, TensorFlow AI & MariaDB)
Executive Summary: PhotoPrism is a privacy-first, Go-powered photo management platform designed specifically for curating, indexing, and searching large existing disk archives of RAW camera photography (
.CR3,.NEF,.ARW), high-resolution JPEGs, and videos. Unlike mobile-first camera-roll replacement apps, PhotoPrism excels at non-destructive indexing of your existing folder hierarchy (/originalsmounted read-only), local TensorFlow image classification & facial recognition, offline GPS reverse geocoding onto interactive world maps, and WebDAV synchronization.
-
Non-Destructive Archive Indexing
Mount your existing curated photo directories at
/photoprism/originals(:ro) without letting the software rename or move your files. -
100% Local TensorFlow AI
Classify landscapes, documents, pets, and faces locally on your Debian server CPU/GPU with zero cloud API data leaks.
-
EXIF & World Map Geocoding
Extract camera EXIF/XMP metadata, lens focal lengths, and GPS coordinates onto an interactive vector world map.
-
Dedicated MariaDB 11 Backend
Replace default SQLite locking bottlenecks on 50,000+ photo libraries with a dedicated UTF8MB4 MariaDB 11 (InnoDB) database container.
1. PhotoPrism vs. Immich: Which Should You Choose?
Both PhotoPrism and Immich are outstanding self-hosted photo platforms, but they target two distinct engineering workflows:
| Workflow Dimension | PhotoPrism | Immich |
|---|---|---|
| Primary Design Goal | Indexing, browsing & tagging existing curated NAS / DSLR / RAW photo archives | Direct Google Photos / iCloud drop-in replacement with native iOS/Android background backup apps |
| File System Philosophy | Respects your existing /originals folder tree untouched (Read-Only supported) |
Manages its own upload storage template (while also supporting External Libraries) |
| Mobile Upload Mechanism | Standard WebDAV (PhotoSync app) or PWA Web UI |
Official native Immich iOS & Android apps |
| Database Engine | MariaDB 11 (InnoDB) |
PostgreSQL + pgvector / VectorChord |
%%{init: {'flowchart': {'wrappingWidth': 600}}}%%
flowchart TB
ORIG["📷 DSLR / NAS Photo Archive<br/>/srv/photos/originals (:ro)"]
PP["🖼️ PhotoPrism Engine (Go)<br/>127.0.0.1:2342<br/>(TensorFlow + Darktable RAW)"]
SIDE["🗂️ /photoprism/storage<br/>Thumbnails & XMP Sidecars<br/>(Local NVMe SSD)"]
DB["🗄️ MariaDB 11 (InnoDB)<br/>UTF8MB4 Metadata Index"]
ORIG -->|"Read-Only Index"| PP
PP <-->|"Writes Previews"| SIDE
PP <-->|"SQL Queries"| DB
2. Production compose.yaml (PhotoPrism + MariaDB 11)
Never use SQLite for a PhotoPrism library exceeding a few thousand photos—concurrent thumbnail generation and TensorFlow indexing require MariaDB 11 tuned with innodb_buffer_pool_size:
# File: /srv/containers/photoprism/compose.yaml
services:
photoprism:
image: docker.io/photoprism/photoprism:latest
container_name: photoprism
depends_on:
photoprism-db:
condition: service_healthy
restart: unless-stopped
user: "1000:1000"
security_opt:
- seccomp:unconfined
- apparmor:unconfined
ports:
- "127.0.0.1:2342:2342/tcp"
environment:
PHOTOPRISM_ADMIN_USER: "nejat"
PHOTOPRISM_ADMIN_PASSWORD: "${PP_ADMIN_PASSWORD}"
PHOTOPRISM_AUTH_MODE: "password"
PHOTOPRISM_SITE_URL: "https://photos.nejat-hakan.de/"
PHOTOPRISM_SITE_TITLE: "NHDOC Photo Archive"
# Never modify or delete files inside /photoprism/originals!
PHOTOPRISM_READONLY: "true"
PHOTOPRISM_ORIG_LINK: "false"
# Database connection to MariaDB 11 container
PHOTOPRISM_DATABASE_DRIVER: "mysql"
PHOTOPRISM_DATABASE_SERVER: "photoprism-db:3306"
PHOTOPRISM_DATABASE_NAME: "photoprism"
PHOTOPRISM_DATABASE_USER: "photoprism"
PHOTOPRISM_DATABASE_PASSWORD: "${PP_DB_PASSWORD}"
# Performance & RAW conversion tuning
PHOTOPRISM_WORKERS: "4"
PHOTOPRISM_JPEG_QUALITY: "90"
volumes:
- /srv/photos/originals:/photoprism/originals:ro
- /srv/containers/photoprism/storage:/photoprism/storage:rw
photoprism-db:
image: docker.io/library/mariadb:11
container_name: photoprism-db
restart: unless-stopped
command:
- --innodb-buffer-pool-size=1G
- --transaction-isolation=READ-COMMITTED
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --max-connections=256
environment:
MARIADB_ROOT_PASSWORD: "${PP_DB_ROOT_PASSWORD}"
MARIADB_DATABASE: "photoprism"
MARIADB_USER: "photoprism"
MARIADB_PASSWORD: "${PP_DB_PASSWORD}"
volumes:
- /srv/containers/photoprism/database:/var/lib/mysql:rw
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
3. CLI Indexing, Face Clustering & Backup Operations
Trigger manual library re-indexing, facial recognition updates, or database backups directly via the photoprism CLI inside the container:
# File: /opt/photoprism-ops/maintenance_commands.sh
#!/usr/bin/env bash
set -euo pipefail
# 1. Index newly added photos in /photoprism/originals without re-scanning existing files
docker exec -it photoprism photoprism index
# 2. Re-cluster facial recognition embeddings and update people tags
docker exec -it photoprism photoprism faces update
# 3. Create a consistent compressed SQL dump of the PhotoPrism MariaDB index
docker exec -it photoprism photoprism backup -i -f /photoprism/storage/backups/photoprism_db.sql
Where to Go Next
Compare PhotoPrism with Immich mobile backups and BorgBackup off-site encryption:
-
Immich Mobile Photo & Video Cloud
Explore Immich for automatic iOS/Android camera-roll backups and CLIP semantic natural-language search.
-
Encrypted Backups with BorgBackup
Protect your irreplaceable
/srv/photos/originalsarchive with deduplicated, encrypted off-site Borg repositories. -
Nextcloud Cloud Sync
Synchronize documents, calendars, contacts, and mobile folders with a hardened Nextcloud stack.