I spent most of today getting PhotoPrism running on my homelab NAS, and it’s the kind of project that looks straightforward until you’re two hours in wondering why the GPU detection isn’t firing. This guide walks through installing PhotoPrism with Docker Compose, the actual commands I ran, and the specific mistakes I made so you don’t have to.
Why Install PhotoPrism Locally
The pitch for PhotoPrism is solid: it’s a privacy-respecting photo library manager that runs entirely on your own hardware. Face recognition, object tagging, location mapping—all of it stays local. No cloud syncing, no subscription creeping up on you, no terms of service changing without notice. You point it at a folder of photos and let the AI do its thing in the background.
I had 40,000 photos scattered across external drives, organized by year but otherwise a mess. Cloud services wanted to charge per gigabyte per month. PhotoPrism ingested the whole library in about 36 hours and now I can search by “dog” or “beach” or even pull up a map of everywhere I’ve taken a photo. The face recognition is decent once it’s trained. It’s not perfect, but it doesn’t need to be perfect to be genuinely useful.
Prerequisites and Hardware
You need Docker and Docker Compose already running. If you’re reading this, you probably have those. I’m running this on an Intel NUC with 16GB RAM and a 500GB SSD for the application. Photos live on a separate NAS mounted via NFS.
Hardware expectations: PhotoPrism is surprisingly efficient. The minimum is probably 2GB RAM and a dual-core CPU, but that’s for a tiny library. For anything over 10,000 photos, bump that to 4GB minimum. The indexing step benefits hugely from extra cores—my 6-core NUC handles indexing about 1,000 photos per minute with face detection on. GPU acceleration (NVIDIA CUDA, Intel Quick Sync) speeds up indexing significantly, but it’s not required. I’m running without it and it’s fine.
You’ll also need a reverse proxy if you want remote access. I’m using Traefik; Nginx works too. Not required if you’re only accessing it locally.
Install PhotoPrism with Docker Compose
Start with a fresh compose file. Here’s what I’m using:
version: '3.8'
services:
photoprism:
image: photoprism/photoprism:latest
container_name: photoprism
restart: unless-stopped
security_opt:
- seccomp:unconfined
- apparmor:unconfined
ports:
- "2342:2342"
environment:
PHOTOPRISM_ADMIN_PASSWORD: ${PHOTOPRISM_ADMIN_PASSWORD}
PHOTOPRISM_ADMIN_USER: admin
PHOTOPRISM_ADMIN_EMAIL: [email protected]
PHOTOPRISM_AUTH_MODE: "password"
PHOTOPRISM_SITE_URL: "https://photos.yourdomain.com/"
PHOTOPRISM_ORIGINALS_LIMIT: 5000
PHOTOPRISM_HTTP_COMPRESSION: "gzip"
PHOTOPRISM_DATABASE_DRIVER: sqlite
PHOTOPRISM_DARKTABLE_PRESETS: "false"
PHOTOPRISM_DISABLE_CHOWN: "false"
PHOTOPRISM_WORKERS: "4"
PHOTOPRISM_LOG_LEVEL: info
TZ: UTC
volumes:
- "./storage:/photoprism/storage"
- "/mnt/photos:/photoprism/originals"
tmpfs:
- /tmp
Before you run this, create a .env file in the same directory with a strong password:
PHOTOPRISM_ADMIN_PASSWORD=your_strong_password_here
The volumes matter here. /photoprism/storage is where PhotoPrism keeps its database and cache—this should be on fast storage, ideally SSD. /photoprism/originals points to your actual photos. I’m mounting an NFS share there, but a local path works fine too.
Now spin it up:
docker-compose up -d
Watch the logs to see it initialize:
docker-compose logs -f photoprism
First startup takes maybe 30 seconds. You should see it create the SQLite database and set up the directory structure. If you see permission errors, the PHOTOPRISM_DISABLE_CHOWN: "false" line might need to change to "true" depending on your setup. I left it as is and it worked.
First-Run Configuration
Head to http://localhost:2342 in your browser. Log in with admin / whatever password you set. The first screen is a quick setup wizard—you can skip most of it.
Go to Settings and make these changes before importing photos:
- Library > Index: Turn on “Automatic indexing” if you want new photos to be picked up in the background. Leave this off for now.
- Library > RAW: Enable RAW file support if you have RAW photos. PhotoPrism can convert them on the fly.
- Advanced > Features: Face recognition is on by default. Disable it if you’re on really limited hardware, but I’d recommend leaving it on.
- Advanced > Performance: Set workers to however many cores you want to dedicate. I used 4 on a 6-core box.
Now point it at your photos. Go to Library > Import and select your originals folder. If you mounted it to /photoprism/originals like I did, PhotoPrism should see it immediately. Don’t click import yet—first, configure what you want it to do.
I left the defaults (move files, create a backup, detect faces) and clicked Import. It took maybe 2 minutes to scan 40,000 photos and queue up the indexing. The actual indexing—where it analyzes images—ran in the background overnight.
The gear I run for this
Hardware from my own homelab, relevant to this guide — direct Amazon links.
Affiliate links — I earn a small commission at no extra cost to you. Browse my full homelab store →
Indexing and Initial Face Detection
This is where patience matters. PhotoPrism will index everything you throw at it, but it’s not fast. My 40,000-photo library took about 36 hours to fully index with face detection running. That sounds long, but it’s a one-time cost.
You can watch progress in the UI under Library > Index. Don’t panic if it says “indexing” for hours—it’s working, just slowly. The logs will show you which photos it’s processing.
One thing surprised me: face detection quality varies wildly. It’s good at detecting faces in clear, well-lit photos. It struggles with side angles, bad lighting, and photos where someone is partially obscured. But once it detects a face, you can manually confirm it, and it learns from corrections. I spent an evening clicking through faces and confirming identities, and it got noticeably better.
While indexing runs, you can browse the library. It shows indexed photos immediately. People and Places sections fill in as the detection runs.
Remote Access with Reverse Proxy
If you only access PhotoPrism from inside your network, skip this. I wanted to access it remotely, so I set up a reverse proxy using Traefik.
I added this to my existing Traefik compose file:
labels:
- traefik.enable=true
- traefik.http.routers.photoprism.rule=Host(`photos.yourdomain.com`)
- traefik.http.routers.photoprism.entrypoints=websecure
- traefik.http.routers.photoprism.tls.certresolver=letsencrypt
- traefik.http.services.photoprism.loadbalancer.server.port=2342
And pointed the service name to my photoprism container. After that, I could hit https://photos.yourdomain.com from anywhere and got the login screen. The password authentication keeps it reasonably secure. If I were really paranoid, I’d add basic auth or a VPN requirement, but PhotoPrism over HTTPS with a strong password is acceptable for my use case.
The key thing: make sure PHOTOPRISM_SITE_URL in your compose file matches whatever domain you’re using. PhotoPrism uses this to generate correct URLs in API calls.
Common Gotchas and Fixes
Permission errors on startup: If PhotoPrism can’t read your originals folder, it’s usually a mount or ownership issue. Run docker-compose exec photoprism ls -la /photoprism/originals to check if it can see files. If not, remount or check your NFS permissions.
GPU not detected: If you have an NVIDIA GPU and want CUDA support, you need to pass the GPU to the container. Add this to your compose file under the photoprism service:
runtime: nvidia
environment:
PHOTOPRISM_DISABLE_TENSORFLOW: "false"
You also need nvidia-docker installed on the host. I didn’t bother—the CPU indexing is fine for my needs, and GPU support adds complexity.
Indexing hangs: If indexing gets stuck on a particular photo, check the logs. Sometimes a corrupted file will cause issues. PhotoPrism is usually good about skipping bad files and continuing, but occasionally you’ll need to manually remove a problematic image and re-run indexing.
Database locks: If you shut down PhotoPrism while it’s indexing, the SQLite database can get into a locked state. Just restart the container and it usually clears up. If not, docker-compose down and wait 10 seconds before bringing it back up.
Memory creep: PhotoPrism’s memory usage can drift upward during long indexing sessions. If you’re on limited hardware, set memory limits in your compose file:
deploy:
resources:
limits:
memory: 4G
What to Do Next
Once indexing finishes, the real value shows up. Search for “beach” and it pulls every beach photo. Search “dog” and it finds your dog photos, even from years ago. The People section lets you click a face and see every photo of that person.
Set up automatic indexing in Settings so new photos are processed as you add them. I drop phone backups into the originals folder every month and PhotoPrism picks them up automatically.
The one thing I haven’t fully figured out is syncing. PhotoPrism supports WebDAV and SFTP for backing up originals, and it has a mobile app that can auto-upload photos. I haven’t tested the mobile app yet—that’s next week’s project. But the core library management is solid.
One more thing: back up your storage volume. It contains the SQLite database and all the indexed metadata. If something corrupts, you’d have to re-index from scratch. A simple cron job that copies the storage folder once a day to another location saved me once when a bad shutdown corrupted the database. It’s worth the paranoia.
FAQ
Can PhotoPrism run on a Raspberry Pi?
Technically yes, but it’s slow. A Raspberry Pi 4 with 8GB RAM can run PhotoPrism, but indexing is very sluggish—expect hours per thousand photos. If you have a small library (under 5,000 photos) and patience, it works. Otherwise, a used x86 NUC or laptop is a better choice.
How much RAM does PhotoPrism need?
Minimum 2GB, recommended 4GB for most libraries. Larger libraries (50,000+ photos) benefit from 8GB or more. The indexing process is the RAM hog—face detection especially will use available memory aggressively.
Does PhotoPrism support video files?
Yes. It handles MP4, WebM, and other common formats. It won’t detect faces in videos, but it will index them by metadata and allow you to browse them in the library. Thumbnail generation is slower for video than photos.
What happens if I run PhotoPrism offline?
Everything works except for features that need external services—primarily reverse geocoding (turning coordinates into place names). All AI detection, search, and browsing works entirely offline. You can access the web UI and manage your library without internet.
Can I use PostgreSQL instead of SQLite?
Yes. Change PHOTOPRISM_DATABASE_DRIVER: sqlite to postgres and add connection environment variables. PostgreSQL is better for very large libraries or if you want backups via traditional database tools, but SQLite is simpler and fine for most homelabs.
Explore PhotoPrism in our AI Homelab Toolkit.