github LibrePhotos/librephotos 1.4.0
1.4.0 - October 2026

3 hours ago

This release makes the edits you make in LibrePhotos reach your files, lets you change the date and place of many photos at once, adds filters to search, and brings album collections and an archive. Updating gets easier too: the work an update leaves now runs from the app. It also fixes a long list of bugs in scans, dates, faces, places and stacks, and a security problem.

Who should update: Everyone. If other people have an account on your LibrePhotos, update right away: this release fixes a security problem.

What's new for you:

  • With Write edits to files on, your tags, captions, locations and rotations now go into your files too, next to ratings, dates and face tags. Write now under Write existing edits to files writes the edits you made before. See What LibrePhotos writes.
  • Select photos and choose Change date and time… or Set location… to fix a camera with a wrong clock, or place a whole trip at once. See Changing the date of many photos.
  • Click Filters on the search results to narrow them by people, camera, date, place, rating or file type. Search also ignores accents now: "Zurich" finds "Zürich". See Search filters.
  • Group your albums into Collections, and use Archive to take photos off the timeline while they stay in your albums. See Collections and Archive.
  • Admins: a Finish the upgrade card in the Admin Area runs the work an update leaves and shows how far it got. See The Finish the upgrade card.

Before you update: Make a backup as usual. The Windows app and the single container with its built-in database now also copy the database by themselves before every database update. See Automatic backups.

After you update:

  • Nothing to do for most of it. The first start takes longer than usual, because LibrePhotos repairs some data once, for example duplicate Nextcloud photos. Admins see the remaining work on the Finish the upgrade card under Account menu > Admin Area: it removes photo details from old thumbnails and reads shutter speeds again by itself. The first Scan after the update checks every file once, so it takes longer too.
  • Recommended for everyone: rescan your photos once. Go to Account menu > Library, then the arrow next to Scan > Rescan. This reads what LibrePhotos did not read before: dates, places and camera details from XMP sidecars, the date of MKV, WebM and phone videos, and Google Takeout files. It is done when the job in Account menu > My Jobs has finished. If you skip it, those details only show up for photos that change.
  • Windows app, west of Greenwich (for example in the Americas): the rescan is required. Up to 1.3.0, the Windows app stored scan times wrongly there, so a normal Scan missed files you had edited.
  • Optional: rescan faces once if face detection ever failed on your server, or if other apps wrote face tags into your photos. Go to Account menu > Library and click Rescan in the Rescan Faces row. If you skip it, faces in those photos stay undetected.
  • If you use stacks: go to Photos > Organizing, open Stacks and click Detect Photo Stacks once. Bursts are grouped better now, and old stacks stay as they were until you do this.
  • If you write face tags to files: face tags of rotated photos (most phone portraits) were written to the wrong place. Go to Account menu > Settings > Metadata Options and click Write now under Write existing edits to files to write them again.
  • Admins: settings, log file and Docker changesNew site settings: Missing photos limit, Scan libraries automatically, Delete photos from the trash after (off by default) and a Features card. New environment variables: STUCK_JOB_HOURS, SIDECAR_PORT_<NAME>, NEXTCLOUD_CA_BUNDLE, FEATURE_VIDEO_PREVIEWS and the thumbnail and video preview sizes; remove DEFAULT_FAVORITE_MIN_RATING from your .env, it is no longer read (see Environment variables). The log file is now librephotos.log: point log shippers and fail2ban filters at the new name (details). The single container Compose file moved to deploy/compose/docker-compose.unified.yml, reads librephotos.env and mounts the database at /var/lib/postgresql: back up the dbcursor folder in your data folder first. The generated secret key moves to protected_media/secret.key; copy it back before you go back to 1.3.0 or older, or everyone has to log in again (details). Scripts that use the API: list endpoints page by 100 with one envelope, errors have one shape, and album ids are numbers everywhere.

Upstream:

🚀 LibrePhotos: Your Edits, Written to Your Files

With Write edits to files set to sidecar or media file, LibrePhotos now writes your tags (as keywords), captions, locations and rotations back to the photo, like ratings, dates and face tags before. The new Write existing edits to files row in Settings > Metadata Options writes the edits you made before you turned it on, as a job with progress and Cancel (#2287). Edits go into the sidecar that already exists (IMG.jpg.xmp from digiKam or darktable) instead of a second one, deleting a face also removes its region from the file, and when ExifTool could not write an edit, an orange toast says so instead of a green "done". Reading got the same care: the scan imports captions, title, creator, copyright and more keywords from IPTC, EXIF and XMP, takes dates, GPS, camera and lens from sidecars and from the sidecars of RAW and video variants, reads Google Takeout JSON files, and recognises LibrePhotos' own writes, so a bulk write no longer turns the next scan into a full rescan. The info panel shows camera, lens, exposure, megapixels and coordinates, owners can open an All metadata view with every tag of the file and its sidecars, and zip downloads include the XMP sidecars. With writing Off (the default), LibrePhotos now really leaves your files alone: a scan never writes to them.

🚀 LibrePhotos: Edit Many Photos at Once

Select photos and choose Change date and time… to shift them all by the same amount (a camera with a wrong clock, or one left on summer time) or give them one date (#2278), or Set location… to place them all at once, optionally copying the position of a photo taken around the same time (#2279). A photo's location can also be removed now. Changed date rules can be applied to the photos already in the library without a full rescan, captions can be generated for the whole library as one job, the files of selected photos can be rescanned and faces detected for one folder or a selection, and photos can be moved with all their files to another folder of the library. An authenticated scan-files API lets an outside process tell LibrePhotos about new files.

🚀 LibrePhotos: Collections, Archive and Groups of People

Albums can be grouped into collections such as Travel or Family, and an album can be in several (#1009). You can upload straight into one of your albums and create an album before it has photos. The new Archive takes photos off the timeline and Memories while they stay in albums, people, places and search (#545), and Photos not in an album shows what is left to sort. Album lists sort by name, count or date, come back to the same place on Back, and events can get a cover you pick. People can be put in groups ("Family", "Work") to filter the People page and the face dashboard by, two people can be merged into one, deleted faces can be restored from a new Deleted tab, and the face dashboard gets a Confirm button on each face and a grid of people with their cover faces.

🚀 LibrePhotos: Search Filters

The search results page has a Filters button: people (photos with all of them together, #329), camera, lens, ISO, aperture, focal length, rating, date taken, location and file type, including "Has a RAW file" (#2259). Active filters show as chips you can remove one by one, and they live in the URL, so a link keeps them and Back undoes the last change. You can also search by filters alone, without any words, through Filter photos in the command palette. Search now ignores accents and letter case in both directions: Zurich finds "Zürich", and Lodz finds "Łódź".

🚀 LibrePhotos: Safer Updates and Scans

The work an update leaves is no longer only in the release notes: a Finish the upgrade card at the top of the Admin Area runs the follow-up tasks and shows their progress, with a dot on the avatar while anything is left (#2322). While the backend updates its database or starts, the web app shows LibrePhotos is updating instead of empty pages, and a small start-up page answers on the backend port from the first second. The Windows app and the single container with SQLite copy the database before every migration, and an older release refuses to start on a database a newer one has migrated. Scans got safer too: a scan of an unmounted or empty library no longer marks every photo missing, an unreadable folder is skipped instead of failing the whole scan, Cancel really stops a job's work, and a long first scan is no longer failed after 24 hours. Admins can also have LibrePhotos scan every library on a schedule from Site settings, with no host cron job (#235).

🚀 LibrePhotos: Security Fix

A logged-in user could change the hash of one of their own photos through the photo API. The hash names the photo's thumbnail files, so rotating the photo afterwards wrote and deleted .webp and .mp4 files outside the thumbnail folders, or replaced another user's thumbnails (GHSA-f7vw-q3m9-4998). The hash is now set by the server only, and every thumbnail path refuses a hash that is not a plain file name. Every API view now also declares who may call it, the token endpoints and failed Basic authentication attempts are rate limited, and the generated secret key is no longer kept in the logs folder. Thanks to shaoyuuuuu for the responsible disclosure.

  • ✨ LibrePhotos: Offline place names: the new ExifTool (offline, built in) Map Provider looks place names up without sending your coordinates anywhere, and it is the default for new installs (existing installs keep their provider). Place names are read by level, so event titles, memories and the location chart name the right town, and Admin Area > Admin Tools > Look up place names again renames every photo after a switch (#2302)
  • ✨ Frontend: The photo page is redesigned as a photo on a mat with a details column and the lightbox actions (favorite, share, download, hide, trash, rotate)
  • ✨ Frontend: The open lightbox photo is in the URL, so Back closes it and a link opens it, and the lightbox asks before it discards an unsaved edit
  • ✨ Frontend: The selection bar only shows while you select, and dangerous one-click actions ask first
  • ✨ LibrePhotos: Trash retention, Empty trash and trashed-on dates on the Trash page
  • ✨ LibrePhotos: Opt-in 1 to 5 star rating mode on the web, read from and written to the file's XMP rating
  • ✨ LibrePhotos: A Documents page, and the photo/video filter on Trash, Recently added, No timestamp and folder pages
  • ✨ LibrePhotos: A view of photos without a location, where Select all selects every one of them (Implemented by err0rgod)
  • ✨ LibrePhotos: Album page menu (rename, sharing, lock, delete), leave an album someone shared with you, person page menu and removing photos from a person
  • ✨ Frontend: Share a public album as a slideshow link (#1457), and public-link photo cards show captions
  • ✨ Frontend: People show as round face crops, and the avatar can be chosen from a person's cover face (Implemented by lalmachado)
  • ✨ LibrePhotos: Event titles are worded in the UI language, and counts, sizes and percentages are full translatable sentences; a new language from Weblate loads without a code change
  • ✨ LibrePhotos: Admins can switch off the animated video previews (#649), set the big thumbnail and video preview sizes, and models for features that are turned off are no longer downloaded
  • ✨ LibrePhotos: Cancel a running upload, drop files anywhere to upload them, and abandoned uploads are cleaned up
  • ✨ Backend: Trust a Nextcloud server with a self-signed or private CA certificate through NEXTCLOUD_CA_BUNDLE
  • ✨ Frontend: Settings and Admin pages get section navigation, clearer explanations of Write edits to files, the default time zone and public sharing, tidier rule tables, an opaque lightbox backdrop and dark-mode maps
  • ✨ Frontend: Accessibility: disabled buttons and menu items say why and stay reachable by keyboard, icon buttons and images have names, and face tiles and job links work from the keyboard
  • ✨ LibrePhotos: The Windows release is smoke-tested before it becomes the latest release, gets a versioned zip name and a SHA256SUMS file
  • ✨ Docs: Step-by-step Windows update, backup, restore and uninstall, an EXIF page true to the code, a text recognition guide, a glossary of the words used in release notes, every environment variable documented, and a note that the new mobile app is not in the stores yet
  • 🔨 LibrePhotos: Dates: a date set by hand always wins over the date rules and can be removed again, MKV, WebM and phone videos get their date, camera and rotation, GPS time zone rules apply on the first scan, upload dates keep the device's local time, and the date picker starts the week on the right day (Implemented by lalmachado)
  • 🔨 Backend: Metadata: sidecars are read under their XMP names, sidecars of RAW and video variants are read, shutter speeds are stored as ExifTool prints them, camera and exposure details are filled, file size comes from the media file and never from a sidecar, and photos store the size they are shown at
  • 🔨 LibrePhotos: Write-back: "Off" is honoured by face tags and every write path, a scan never writes metadata, person names are added to keywords instead of replacing them, rotations go into sidecars as XMP-tiff:Orientation, face regions of rotated, HEIC and AVIF photos land in the right place, and edits made through the metadata API reach the app
  • 🔨 Frontend: Multi-word keywords stay whole in the keyword editor (Implemented by lalmachado)
  • 🔨 Backend: Faces: photos the face service failed on are retried, faces are detected in photos that already have XMP face regions, Cluster Selection Epsilon changes the clusters again (#1996), and faces without a crop no longer cause errors and get their crop back on Scan Faces
  • 🔨 Frontend: Faces dashboard: tab counters match the lists, and the "Unknown - Other" group no longer offers Confirm all, rename or delete
  • 🔨 Backend: Scans: exact copies of a photo are no longer processed again on every scan, a photo whose copy is still on disk is not missing, removing a scan job no longer forces the next scan to read every file (#1104), and 3GP videos with only 3GPP brands are accepted again
  • 🔨 Backend: Places: GPS added, changed or removed in a file reaches the map and Places, a moved photo forgets its old place name when the new one cannot be looked up, and a spot with no place name (open sea) is no longer looked up again and again or shown as a failure
  • 🔨 Backend: Stacks: burst detection reads Canon drive modes, groups per camera by sub-second time, keeps frames without a serial number in their burst, and "highest resolution" never picks a photo of unknown size
  • 🔨 Backend: Nextcloud: repeated scans no longer duplicate photos, and copies follow changes and deletions on the server
  • 🔨 Backend: Jobs: Cancel stops scans, detection, training and embeddings, a cancel survives face clustering progress, stuck-job cleanup counts from a job's last progress (STUCK_JOB_HOURS), and starting a job twice no longer queues a second copy
  • 🔨 LibrePhotos: Users whose scan folders overlap each get their own photo of a shared file, so neither is refused their own photo or sees the other's rotation (#1957)
  • 🔨 Frontend: A list that fails to load shows an error with Retry instead of the empty state, failed saves are reported once, success is only shown after the server answered, and unsaved settings ask before you leave
  • 🔨 Frontend: Select all on "Photos without Timestamps" selects every undated photo, thing albums stay reachable when their covers are hidden, folders with more than 100 subfolders show all of them, the Sharing overview shows and counts your public photos, and valid email addresses are accepted
  • 🔨 Frontend: Colours set with light-dark() no longer vanish in light mode in production builds (Implemented by dotanm)
  • 🔨 Frontend: No stray spinner on page load, one page gutter on every route, the photo grid lays out again when its width changes, and the rotate toast names the direction you chose
  • 🔨 Backend: The seekable copy of a transcoded video starts once the live conversion is far enough ahead (#2002), and zip downloads include the XMP sidecars of a photo's files
  • 🔨 Backend: Windows app: one instance per data folder with tray status from the start, it names the program holding its port instead of quitting silently, sidecar starts are verified, and scan times are stored as real UTC
  • 🔨 Backend: Django is set up off uvicorn's event loop, which fixes a start-up error on some deployments, and backend images share one entrypoint with pinned database and package versions
  • 🔨 Mobile: Face boxes are placed at the right scale, and people keep their covers after a sync
  • 🔨 LibrePhotos: Leftover settings with no effect are gone (LLM-era caption options, the face confidence settings, DEFAULT_FAVORITE_MIN_RATING)
  • 🔨 LibrePhotos: Code health: the backend is type-checked with mypy and split into domain packages, every list endpoint answers in a constant number of queries, every response is declared in the OpenAPI schema (#485), external tools run with timeouts, and dead code is checked in CI
  • 🔨 Frontend: Code health: the web and mobile apps share one API client with one query-key factory, strict TypeScript without a baseline, ESLint as a hard gate, forms on @mantine/form with zod, typed translation keys, and lint rules for lists, dialogs, toasts and styling
  • 🔨 LibrePhotos: Tests and CI: factories for backend test data, real ExifTool round-trip tests for every write path, checks for model and migration drift, formatting, the OpenAPI schema against the zod schemas, the supported Python range, and fixes for tests that timed out under load
  • ✨ LibrePhotos: Renovate dependency updates for the backend, frontend, mobile app and docs, among them ESLint 10.13, TanStack Query 5.104, Playwright 1.64, zod 4.6, i18next 26.4 and pillow-heif 1.8.1, plus better-sqlite3 13 and @types/node 24 for the mobile app
  • 🔒 Backend: Every API view declares who may call it, and the token endpoints and failed Basic authentication attempts are rate limited
  • 🔒 LibrePhotos: Auth cookies are set in one place with explicit HttpOnly, SameSite and Secure flags
  • 🔒 Backend: The generated secret key is kept out of the logs folder, which people zip for bug reports
  • 🔒 Backend: The last hand-parsed views read their input through serializers, and ?public=false is no longer treated as public
  • 🔒 Backend: A photo's hash can no longer be changed through the photo API, and thumbnail paths always stay inside the media folder (GHSA-f7vw-q3m9-4998)

Full changelog: 1.3.0...1.4.0


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

Read this release on the docs site: https://docs.librephotos.com/blog/1-4-0

Don't miss a new librephotos release

NewReleases is sending notifications on new releases.