Deployment guide

Host PhotoPrism on Your Own VPS

Deploy on a VPS Cloud →

Tutorial

Self-hosting

Host PhotoPrism on Your Own VPS

PhotoPrism is a self-hosted photo manager that automatically sorts your pictures by location, content and color thanks to embedded machine learning. On your own VPS, it turns a disorganized collection into a searchable library, without any external cloud service. This guide covers full installation, nginx reverse proxy configuration, ML performance tuning on a modest VPS, troubleshooting common errors, and a solid backup strategy.

8 min read5 steps
Contents· Why self-host PhotoPrism on a VPS1/9
  1. 01Why self-host PhotoPrism on a VPS
  2. 02The strengths of a self-hosted PhotoPrism
  3. 03Exact requirements: RAM, CPU, storage and networking
  4. 04Deploy PhotoPrism step by step
  5. 05Mobile access: iOS and Android app
  6. 06ML optimization on a VPS without GPU: key variables
  7. 07Troubleshooting: the most common errors
  8. 08Backup strategy: originals, storage and database
  9. 09PhotoPrism or Immich: which to choose

Why self-host PhotoPrism on a VPS

Where Immich aims for the full cloud experience with real-time mobile backup, PhotoPrism focuses on indexing and organizing an existing photo library, often large, sitting on a file system. It analyzes your folders, sorts photos by labels (mountain, beach, car), detects faces, reads EXIF and geocodes pictures. All of this runs locally via embedded TensorFlow. Hosting it on a VPS has a twofold benefit: centralizing photo archives scattered on a disk accessible from anywhere, and getting automatic tagging without ever transmitting the images to a third-party service. PhotoPrism works with MariaDB for good performance and directly reads an originals folder that you fill via WebDAV, rsync or a mount.

The strengths of a self-hosted PhotoPrism

  • Automatic tagging by local AI: search by keywords, locations and colors without a cloud.
  • Indexing of existing archives placed on disk, via WebDAV, rsync or network mount.
  • Detection of duplicates and similar photos to clean up a cluttered collection.
  • Interactive maps based on the GPS coordinates extracted from EXIF metadata.
  • Read-only mode available: PhotoPrism indexes without ever modifying your original files.
  • Installable PWA interface, accessible from any browser on your network.

Exact requirements: RAM, CPU, storage and networking

PhotoPrism is memory-hungry as soon as TensorFlow is involved. Here are the thresholds to respect to avoid unpleasant surprises. Without a GPU and with the ML engine active, plan for 2 GB of RAM minimum: below that, the indexing process triggers an OOM (out of memory) and the container restarts in a loop. With ML fully active (face detection, scene classification), 4 GB of RAM are recommended — the comfortable level for a library of 10,000 to 50,000 photos. A minimum 1 vCPU is enough for browsing, but the initial indexing of a large archive benefits from 2 to 4 cores: PhotoPrism distributes work via PHOTOPRISM_WORKERS. The default port is 2342; remember to open it temporarily for a first test before placing the reverse proxy. On the storage side, provision your originals (the actual size of your library) plus 30 to 40% for multi-resolution thumbnails, cache and the MariaDB index. A library of 100 GB of originals typically generates between 20 and 35 GB of derived data. Add a 2 GB minimum swap partition if your VPS has less than 4 GB of RAM: it absorbs indexing spikes without crashes, at the cost of slight slowness.

Deploy PhotoPrism step by step

  1. Prepare the VPS and volumes

    Install Docker, then create two persistent folders: /opt/photoprism/originals for your photos and /opt/photoprism/storage for the cache and the index database. Separating these volumes simplifies backups.

  2. Declare PhotoPrism and MariaDB

    In docker-compose.yml, add a mariadb service with a dedicated volume and a photoprism service (image photoprism/photoprism:latest). Link them via PHOTOPRISM_DATABASE_DRIVER=mysql and point PHOTOPRISM_ORIGINALS_PATH to your photo folder.

  3. Set the administrator and the indexing mode

    Set PHOTOPRISM_ADMIN_PASSWORD and enable PHOTOPRISM_READONLY=true if you want it to index without touching the originals. Then launch the stack with docker compose up -d.

  4. Import and run the first indexing

    Copy your photos into the originals folder via rsync or WebDAV, then trigger the analysis from the interface or on the command line with docker compose exec photoprism photoprism index. This is where the AI generates labels and thumbnails.

  5. Configure nginx as a reverse proxy with SSL Certbot

    Install nginx and certbot on the host. Create a server block in /etc/nginx/sites-available/photoprism.conf that listens on port 80, redirects to 443, and proxies to http://127.0.0.1:2342. Add the directives proxy_set_header Host $host, proxy_set_header X-Real-IP $remote_addr and proxy_set_header X-Forwarded-Proto $scheme so PhotoPrism correctly detects the HTTPS scheme. Obtain the certificate with certbot --nginx -d gallery.yourdomain.com. Automatic renewal is handled by the certbot systemd timer — check with systemctl status certbot.timer.

Mobile access: iOS and Android app

PhotoPrism does not offer a native automatic backup app like Immich, but its PWA (Progressive Web App) interface is mobile-optimized: add it to your home screen from Safari (iOS) or Chrome (Android) for a near-native experience. For browsing and searching, this is sufficient. If you want access outside the local network, enable HTTPS (see the reverse proxy step): the PWA only installs in a secure context. For backup from the phone, the usual complementary solution is Syncthing or rclone: configure them to send the DCIM folder to /opt/photoprism/originals on the VPS, then schedule automatic re-indexing via a cron running docker compose exec -T photoprism photoprism index --cleanup.

ML optimization on a VPS without GPU: key variables

On a standard VPS (no dedicated GPU), TensorFlow runs in CPU mode. Three environment variables let you adjust the quality/resource balance. PHOTOPRISM_WORKERS controls the number of parallel indexing threads — set it to the number of available vCPUs (2 or 4) to speed up initial indexing without saturating the CPU. On a single-vCPU VPS, force it to 1 to avoid contention. PHOTOPRISM_DISABLE_TENSORFLOW (value true) completely disables the ML engine: no more automatic classification or face detection, but indexing is 3 to significantly faster for indexing and RAM consumption drops below 1 GB. Useful for an initial migration of a very large archive — re-enable ML afterwards to re-run the analysis. PHOTOPRISM_DETECT_NSFW (value true) enables the inappropriate content filter, at the cost of an extra model pass on each image: only enable it if you genuinely need it, as it significantly slows indexing. For VPSes between 2 and 4 GB of RAM, the recommended configuration is PHOTOPRISM_WORKERS=2, PHOTOPRISM_DETECT_NSFW=false and TensorFlow active — you get ML without OOM risk.

Troubleshooting: the most common errors

OOM (Out of Memory) at startup or during indexing. This is the first error on VPSes with 1 GB of RAM. The Docker log signature: Killed or signal: killed on the photoprism process. Immediate fix: add swap (fallocate -l 2G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile) and add the line to /etc/fstab for persistence. Lasting fix: move to a VPS with at least 2 GB. Endless indexing that never finishes. On a library of 50,000 photos on a 2-vCPU VPS, the first complete indexing (with ML) can take 6 to 24 hours. This is normal. Follow progress in the logs with docker compose logs -f photoprism. If the process seems stuck for more than an hour, check CPU usage: docker stats photoprism. If it is at 0%, restart the service. Permission problems on volumes. PhotoPrism runs as UID 1000 by default. If /opt/photoprism/originals is owned by root, access silently fails. Fix with chown -R 1000:1000 /opt/photoprism. Also verify that the storage volume is writable: this is where PhotoPrism writes thumbnails and the local index. Interface loads but photos do not appear. Check that indexing was actually triggered (photoprism index) and not just the service started. The initial import is not automatic.

Backup strategy: originals, storage and database

An effective PhotoPrism backup distinguishes three components with different needs. The originals folder contains your master files (JPEG, RAW, videos): this is the irreplaceable part. Back it up first with an off-site copy — rclone sync /opt/photoprism/originals remote:backup/originals to object storage (Backblaze B2, S3) works well and supports incremental resumption. Recommended frequency: daily. The storage folder contains generated thumbnails, cache and sidecar data. It can be fully rebuilt by re-indexing (photoprism index --force): back it up only if you want to avoid re-running a long indexing after an incident. A weekly snapshot is sufficient. The MariaDB database stores the metadata index, albums, manual labels and identified faces — all the curation work you have done manually. Back it up separately with a regular dump: docker compose exec mariadb mysqldump -u photoprism -p photoprism > /opt/backup/photoprism-db-$(date +%Y%m%d).sql. Schedule this dump in cron and encrypt it before off-site transfer. If you use SQLite instead of MariaDB, simply copy the .db file after stopping PhotoPrism to guarantee consistency.

PhotoPrism or Immich: which to choose

Scroll the table

CriterionPhotoPrismImmich
Main objectiveIndex and organize an existing photo libraryFull photo cloud with mobile backup
Mobile appPWA, no native automatic backupiOS/Android apps with automatic backup
DatabaseMariaDB or SQLitePostgreSQL with vector extension
AI searchTensorFlow labels by content and colorSemantic search and advanced facial recognition
Recommended RAM4 GB (2 GB minimum without GPU)6 to 8 GB
File modelReads an originals folder, read-only mode availableManages its own upload tree
Multi-userLimited, geared toward a single collectionIsolated libraries per user
Ideal use caseArchive and search large photo foldersReplace Google Photos day to day

Mount your originals folder read-only (:ro in the Docker volume) and enable PHOTOPRISM_READONLY=true. PhotoPrism will index and generate labels and thumbnails in the separate storage volume, without ever risking modifying or deleting an original file. This way you can safely point to a precious archive, and even synchronize that folder via rsync from another server.

Organize your photo archives with full autonomy

The ServOrbit Cloud VPS provides the RAM, storage and Docker required by PhotoPrism, its TensorFlow engine and MariaDB. Index thousands of pictures and find them again in a few keywords, without a third-party cloud.