This release brings a standalone Windows build that needs no Docker, moves every machine learning model to ONNX Runtime and drops PyTorch, brings Memories, makes scanning and the web app a lot faster, and closes a long list of security issues: all multi-user instances should upgrade. Here is an overview of all the new features, improvements, and bug fixes since the last release.
Before you upgrade: the first 100 database migrations are now combined into one. Installs on 2026w10 or later (including every 1.x release) upgrade directly as usual. Installs on an older release must first run 1.1.0 once so its migrations complete, then upgrade to this release. If you skip that step, the migration stops with a hint that names the upgrade path. See Upgrading from a release older than 2026w10.
Upstream:
🚀 LibrePhotos: Standalone Windows Build, No Docker Needed
LibrePhotos now runs on a plain Windows PC: download librephotos-windows-x64.zip from the assets of this release, unzip it anywhere, and double-click librephotos.exe. That one program is the web server, the web app, the background workers, and every machine learning service, with ExifTool and ffmpeg bundled next to it. There is nothing else to install: no Python, no Docker, no database server. The database is SQLite in %LOCALAPPDATA%\LibrePhotos, your photos stay where they are, and LibrePhotos lives in the notification area with an icon to open it, open the data or log folder, and quit (which stops everything it started). Your browser opens on the first-time setup, and the models download on the first scan. It is meant for one person's library on their own PC; for a shared server or PostgreSQL, keep using one of the Docker deployments. See the Windows standalone guide for options such as --data-dir and --photos.
🚀 LibrePhotos: Every ML Model on ONNX Runtime, PyTorch Removed
Tagging, captioning, semantic search, and face recognition now all run on ONNX Runtime. PyTorch, sentence-transformers, transformers, and llama.cpp are gone from the images, which makes them much smaller and faster to build. Default tagging now uses MobileCLIP-S2 zero-shot over the shared tag vocabulary instead of Places365, semantic search keeps the same CLIP ViT-B/32 weights in ONNX form, and captioning is a single small vision-language model (LFM2.5-VL-450M) that is always available and is prompted with the people and places in the photo, so captions can name who is in the picture and where it was taken. This replaces the BLIP captioner (broken for about a year), im2txt, Moondream, and the optional Mistral caption polish. The GPU image now actually runs the models on the GPU, ML services only start when their feature flag is on, and idle services unload their models to free memory.
🚀 LibrePhotos: Memories
A new Memories page shows the photos you took on this day in earlier years (#844). There is one tile per year with a cover, the place, and how many photos it holds; clicking a tile plays that year as a slideshow, "Play all" plays every year in a row, and a gallery switch shows the whole day across the years as one grid. Memories cover the anniversary plus or minus three days, roll over at your own midnight, and scale with the number of years in your library, not the number of photos. Nothing is injected into the timeline and nothing autoplays: if you do not want memories, you simply never open the page. (Implemented by dotanm)
🚀 LibrePhotos: Faster Scans and a Faster Web App
The scan pipeline was profiled end to end. The metadata service now reads every tag of a request in one ExifTool command instead of one per tag (about 390 ms down to about 30 ms per photo), thumbnails are decoded once per photo instead of three times, RAW files use the camera's embedded preview when it is good enough, every file is hashed once, face detection keeps the embeddings it already computed, and backend services are reached on 127.0.0.1 instead of localhost, which cost two seconds per request on some systems. In the browser, route code splitting now actually splits routes and translations load on demand, so the main JavaScript chunk shrinks from 3.68 MB to 829 KB. The photo grid moved to TanStack Virtual and drag and drop to dnd-kit.
🚀 LibrePhotos: Security Hardening
A thorough authorization review of the backend closed a series of cross-user leaks. User albums and the select_all download could be used to place another user's private photos in your own album and share them again; albums can now only vouch for their owner's photos. Three reported issues are fixed and published: a photo hash existence oracle (GHSA-hq2w-x39h-8wmp), the map API key being returned to anonymous callers (GHSA-6367-m327-c4pf), and other users' job records in /api/rqavailable/ (GHSA-975v-mx44-9jxq). On top of that: day albums, read-only album shares, folder paths, photo metadata lookups, chunked uploads, zip downloads, and hash-keyed photo lookups are scoped to the requesting user; public albums honour their location and time sharing settings; and password reset links can no longer be poisoned through the Host header. Thanks to Hama1cco for the responsible disclosure.
- ✨ LibrePhotos: Revocable per-photo share links: the lightbox share button now creates a link for a single photo that you can copy, replace, or revoke from the Public Links page (#2028) (Implemented by jonesfionn101-dotcom)
- ✨ Frontend: Add a face the scan missed by drawing a box on the photo and naming the person in the usual Label faces dialog (Implemented by dotanm)
- ✨ Frontend: Tag a whole selection at once: select photos anywhere in the grid and press
t(Implemented by dotanm) - ✨ LibrePhotos: Transcoded videos are now seekable (the converted file is cached after the first play), and the arrow keys seek in the lightbox (#1983) (Implemented by dotanm)
- ✨ Frontend: Copy a photo to the clipboard from the lightbox with the Copy button or Ctrl/Cmd+C
- ✨ Frontend: A floating upload progress card shows every file of an upload and its state
- ✨ LibrePhotos: Optionally give each new user their own data folder (
AUTO_CREATE_USER_DIRECTORY, off by default) (#2038) (Implemented by dchaudhari7177) - ✨ Backend: Photo descriptions are imported from XMP (
dc:description) (Implemented by jdmasa) - ✨ LibrePhotos: Native install on every platform: the backend installs with a plain
pip installeverywhere, with libvips, LibRaw, ExifTool, and ffmpeg all coming from wheels, and the base Docker images shrink accordingly - ✨ LibrePhotos: The official mobile app is rebuilt from scratch with Expo and an offline-first sync engine, backed by new delta-sync endpoints on the server; it replaces the old React Native app in the repository
- ✨ Frontend: Library counter tiles are abbreviated on mobile, with the exact value in a tooltip (#973) (Implemented by krisnaparahita)
- ✨ Backend: Per-module log levels with
LOG_LEVELS=package=LEVEL,... - 🔨 LibrePhotos: HDR videos from phones are tonemapped instead of playing washed out
- 🔨 LibrePhotos: The live video transcode is bounded and no longer hangs on a full pipe (#1920) (Implemented by dotanm)
- 🔨 LibrePhotos: Faces: hand-drawn and XMP face regions no longer receive a stranger's embedding, face counts count inferred faces correctly, unnamed and deleted faces are shown truthfully, the face dashboard no longer requests pages that moved faces removed, and antelopev2/buffalo_m model zips unpack correctly (Implemented by dotanm)
- 🔨 LibrePhotos: Scanning: files replaced in place are re-indexed, per-file scan failures are recorded on the job, XMP sidecars stay with their photo, symlink loops are handled, job progress is race-free, and failed photos no longer starve the CLIP embedding job
- 🔨 LibrePhotos: Folders and scan directories: a folder no longer absorbs a sibling whose name it prefixes, a directory that only shares the allowed root's prefix is refused, a scan directory another user already scans is rejected, and scan directories are validated on user creation (Implemented by dchaudhari7177)
- 🔨 LibrePhotos: Photos marked public are served to anonymous visitors again, and a
MEDIA_FILErotation is no longer applied twice on rebuild (Implemented by dchaudhari7177) - 🔨 LibrePhotos: Orphaned thumbnail files are removed (Implemented by stepheng223)
- 🔨 LibrePhotos: Database: the album photo indexes and unique constraints lost in migration 0099 are restored, and tag photo counts stay correct when photos stop counting (Implemented by dotanm)
- 🔨 LibrePhotos: Revoking a public album link and sharing again now mints a new link
- 🔨 LibrePhotos: The people and user album lists no longer run one query per album, and place albums show before the map reports its bounds
- 🔨 Frontend: Password managers can remember login credentials again (#924), video tiles no longer download whole clips as images, album covers no longer autoplay every video, and the timeline stays populated after an upload
- 🔨 Frontend: Single-flight token refresh: concurrent requests share one refresh and an expired session logs out once, plus a series of accessibility and translation fixes
- 🔨 LibrePhotos: Captioning reports the service's real error, uses the q4 model export that works on all CPUs (including ARM64), and starts the model download when a caption is requested too early
- 🔨 LibrePhotos: The Windows job cluster works again with django-q2 1.11.1, ML services honour
BASE_DATA, and a busy ML service is no longer killed by the watchdog mid-request - 🔒 LibrePhotos: Photo edits and deletes through the photo API are owner-only, staff included: administrators can no longer edit or delete other users' photos
- 🔒 LibrePhotos: Nextcloud server addresses are validated on every use (loopback, link-local, and reserved addresses are refused) and the download is hardened; set
NEXTCLOUD_ALLOW_PRIVATE_ADDRESSES=falseto restrict users to Nextcloud servers on the public internet - 🔒 LibrePhotos: The cover-face and incomplete-faces queries are scoped to the requesting user (Implemented by dchaudhari7177)
- 🔒 LibrePhotos: On the unified and standalone images, password reset mail is only sent when the address is trusted: set
FRONTEND_BASE_URL(or list your origin inCSRF_TRUSTED_ORIGINS) to keep self-service password reset working - 🔒 LibrePhotos: A security policy for private vulnerability reporting (Implemented by dchaudhari7177)
- ✨ LibrePhotos: Hardened release pipelines (Implemented by Totara-thib), release-pinned base images, a backend refactor of every function with a CRAP score above 30 (tests first), a Playwright end-to-end suite, a frontend type-check ratchet, Docusaurus 3 for the documentation site with a redesigned landing page, and many Renovate dependency updates
If you are interested in how I develop this application, you can watch me develop features live on my channel
Sponsoring development:
My work is sponsored by 1 awesome person!
If you like the work I do, then you can support me via GitHub sponsors or via PayPal