Pick PhotoPrism when you want a photo library with face and object tagging that runs as one Go binary and idles low: the Community Edition is AGPL-3.0, ships as `photoprism/photoprism:ce`, and defaults to SQLite, so a single container is a complete install — we measured 102 MB of idle RAM and 3.2 GB on disk on a GCP e2-standard-2 on 2026-08-28. Indexing is the other story: upstream asks for 3 GB of RAM plus 4 GB of swap, and the `latest` tag is not the AGPL image. Immich is the catalog's other photo library; PhotoPrism is the one built around a folder of originals you already have.
What you need
Any VPS with at least 2048 MB of RAM
A domain you control — most self-hosted setups need HTTPS in front of them
Paid link — we earn a commission if you shop through it.
Install
Run these commands on your server:
# PhotoPrism — official docker run (docs → Getting Started → Docker), on the :ce tag# :ce is the AGPL Community Edition image; :latest bundles the Plus extensions under PhotoPrism's Plus LicensePP_PASS=$(openssl rand -base64 18) && echo "admin password: $PP_PASS" # 8–72 chars; never a default like 'insecure' on a public serverdocker run -d \ --name photoprism \ --security-opt seccomp=unconfined \ --security-opt apparmor=unconfined \ -p 2342:2342 \ -e PHOTOPRISM_UPLOAD_NSFW="true" \ -e PHOTOPRISM_ADMIN_PASSWORD="$PP_PASS" \ -v ~/PhotoPrism/storage:/photoprism/storage \ -v ~/Pictures:/photoprism/originals \ photoprism/photoprism:ce# /photoprism/storage holds the SQLite index, config, cache, thumbnails — it must always be mounted or you lose them on recreate# open http://SERVER_IP:2342 — user admin, password printed above
PhotoPrism's getting-started pages are explicit about memory, the storage volume and the two image tags:
non-negotiableOur snippet mounts only originals. Upstream says the storage folder "is used to save config, cache, backup, thumbnail, and sidecar files" and "must always be mounted so that you do not lose these files after a restart or upgrade" — with the default SQLite driver the index database lives there too. Add ./storage:/photoprism/storage, and "Never configure the storage folder to be inside the originals folder unless the name starts with a .".
non-negotiableUpstream wants "at least 2 cores, 3 GB of physical memory, and a 64-bit operating system" plus "at least 4 GB of swap", and says not to set a hard memory limit "as this can cause unexpected restarts when the indexer temporarily needs more memory to process large files". Idle is cheap (102 MB measured); indexing RAW files and panoramas is what spikes, and the FAQ's answer for low-resource hosts is fewer indexing workers and smaller file limits, not a cgroup cap.
non-negotiablePHOTOPRISM_ADMIN_PASSWORD is the initial superadmin password (8–72 characters) and upstream says "Never use easy-to-guess passwords or default values like insecure on publicly accessible servers" — our snippet's changeme is the same class of value. Set PHOTOPRISM_SITE_URL to the real scheme://host[:port]: it is the canonical URL "used in generated links and to determine HTTPS/TLS" and defaults to http://localhost:2342/.
non-negotiablephotoprism/photoprism:latest is not the pure-AGPL build: the FAQ says use "the ce tag instead of latest to get a slightly smaller Docker image distributed under the AGPL", while latest bundles Plus-licensed extensions, and several config options (HTTPS security headers, rate limiting, SVG support) are marked plus-only. :preview is the development build. Both ce and latest are multi-arch for 64-bit amd64 and arm64 only.
non-negotiableSQLite is the default, but the FAQ says "If you are looking for scalability and high performance, it is not a good choice" and upstream's compose example ships MariaDB (mariadb:12.3; 10.5.12+ required). A backup must include originals and "a copy of the index database" — docker compose exec photoprism photoprism backup -i -f writes it under storage/backup/ — and ideally the whole storage folder. Upstream asks for 3 GB of RAM + 4 GB swap; our 2 GB figure is below that, and the 102 MB we measured is idle only — size for indexing, not idle.