All your photos, on your own machine, findable.

meerpic is a self-hosted photo browser for large collections: tens of thousands of files, in as many folders as you keep them. It reads every file's own metadata into PostgreSQL, so the photos sort by when they were actually taken, sit on a map where they were taken, and can be found by what is in them: type cows and you get cows. Nothing leaves your machine; the search model runs locally too.

Newest first: the photo you took a minute ago is top-left, under the day it was taken.

What makes meerpic different

Built for a library that has outgrown the file manager: tens of thousands of files, in as many folders as you keep them, and all of them yours.

Search by what is in the picture

Type it: cows, birthday cake, receipt. Every photo is turned into a vector by SigLIP 2, a model trained to put an image and a sentence describing it close together, and a search is the photos nearest to what you typed. It reads German as well as English: kühe auf der weide works.

It runs on your CPU through ONNX Runtime: about 0.1 s per photo to index on a laptop, and a few tens of milliseconds per query. Results come by relevance; one switch orders the same matches by date.

More like this one

Press s on a picture of a sheep and you get the other sheep. Find similar is the photos nearest to another photo, and it is a filter like any other, similar:1234, so it combines with everything else: similar:1234 2023 is the ones from that year.

The words in a picture

text:rechnung lists every photo and video with that word in it: a letter, a receipt, a sign, a screenshot, a slide in a video. The info panel shows what was read, with your words marked, and Copy text puts all of it on the clipboard.

PaddleOCR reads it on the CPU, umlauts and ß included, and nothing leaves the machine. Screenshots and saved pictures are read first, because that is where the text is.

The same person

The info panel shows the faces in a photo. Click one and you get every other picture with that person in it, closest first. It is a filter too, face:5678, so face:5678 2019 is that person in 2019.

Nobody is named and nothing is grouped ahead of time: a face model on your CPU turns each face into a vector, and the list is the faces nearest to the one you clicked. Its weights are licensed for non-commercial use only.

The dates are in the files

A file manager sorting by "created" sees when a file landed on your disk, not when it was taken: after a sync, the same second for twenty thousand photos. The real capture time is inside each file, usually with the time zone it was taken in. meerpic reads it, falls back to the file name, then to the file time, and remembers which it used, so a photo dated by guesswork says so. On a real library:

25,095files 20,474with a capture time 5with nothing at all

A map, and places offline

The map (M) shows every photo with a position, clustered, with a thumbnail per cluster. Pan to a place and press Show in grid for the photos in view, or type in:potsdam. Coordinates become town, region and country with a copy of GeoNames that ships with the agent: no geocoding service sees where you have been. The map tiles are the one thing the browser fetches from elsewhere, and the tile server is a setting.

A filter bar that types like meercal's and meerail's

Plain words search by meaning. Everything else narrows, and all of it combines: in:potsdam is:video 2024 is the videos you took in Potsdam that year. Dates are the time the photo was taken, where it was taken. The places are offered as you type.

in:potsdamthe town, region or country
is:videowhat it is: also is:photo, is:screenshot
is:liveLive Photos
2024a year, or month:2024-07, on:2024-07-22
after:2024-03a range, with before:; a year, a month or a day
camera:iphonethe camera's make or model
album:"Urlaub Polen"an iCloud album; is:favorite for the hearts
similar:1234nearest to that photo
text:rechnungwritten in it, anywhere, in any case

Every folder, one timeline

Point it at as many folders as you keep photos in: the phone's library, a folder of camera imports. They show as one timeline, newest first. Each folder has a name, so it can move on disk without a re-index.

Indexing goes newest first too: the last few days are browsable within a minute, searchable a little after, and the rest fills in behind. The first index is the expensive part; after that, only new photos cost anything.

iPhone videos that play

An iPhone records HEVC, which no browser on Linux decodes. The agent runs a full ffmpeg and gives every video an H.264 copy at 720p, tone-mapped from the iPhone's HDR, so it plays everywhere. On a real library the copies came out at 5 to 10% of the originals' size, and the original is untouched and one click away.

Rest the pointer on a video and it plays in place, muted. Until its copy exists, moving across the tile flicks through ten frames from the whole clip, so a ten-minute video is as quick to check as a ten-second one.

One moment, one photo

A Live Photo arrives as a HEIC and a two-second MOV. Shown naively, every moment appears twice. meerpic pairs them: the still is the photo, and the motion plays when you rest on its LIVE badge. An edited photo arrives as a second file, IMG_1925-edited.heic; as in Photos, the edit is what the grid shows and the original is one click behind it. On the library above:

5,772videos out of the grid 552edit pairs

Into a mail, as a JPEG

Once it is in your library, the photo you just took is top-left. Drag it out of the desktop app into a mail and the mail gets a real file: the original when it is a JPEG, a JPEG made from it when the phone wrote HEIC, because the person you are writing to may not be on an Apple device. In a browser tab, c copies it and d downloads the JPEG. One setting takes the GPS position out of everything you hand over.

Delete that shows its work

The dialog lists every file move before it happens, one per file, and only that list runs. Files go to a trash folder in the library, emptied after 30 days. Deleting only happens on this computer: a photo synced from iCloud stays in iCloud and on your iPhone, because rclone can read iCloud Photos but not change them, and the next sync does not bring it back.

IMG_1925.HEICthe photo IMG_1925.MOVits motion

The server never changes a photo

meerpic splits in two, the way meercal and meerail do. The agent does the work: it walks the library, reads the metadata with exiftool, draws thumbnails, converts videos and runs rclone when you press Sync. The server is the web layer: it reads the database and leaves a note for the agent when you want something done. Nothing is ever written to a photo.

iCloud Photos, if that is where your photos are

Folders on your disk need no sync at all. iCloud Photos is the first sync built in: one Sync button (or .) runs the rclone copy you would otherwise type, with a progress bar, and the new photos land in your library. copy, not sync: nothing is ever deleted on either side.

After every sync the agent also reads what iCloud knows and the files do not: a heart set on the phone, the Hidden album, Recently Deleted and every album, WhatsApp's included. Nothing is downloaded for it, and Hidden and Recently Deleted stay out of every listing unless you ask for them.

Sync Favourites Albums
Hidden Recently Deleted

Pictures you did not want

is:nsfw lists what a classifier made for exactly this scores as explicit, most certain first, and it looks at the pictures apps saved before anything else. Those tiles are blurred until the pointer is on them. It runs on your CPU, and it is a ranking, not a verdict: look before you delete. When it is wrong, n marks a photo safe.

Everything in PostgreSQL

One row per file, with where it is, when and where it was taken and what took it; one search vector per photo, in pgvector beside everything else; the albums; the deletes. Every column is derived from the file and can be rebuilt, and every file in the cache can be deleted and will be made again. make psql gives you a shell on it.

Keyboard first, light and dark

Full keyboard control, from search to delete, and ? lists every key. Light and dark, following the system or pinned.

/ Enter x s m i d c . Del g f Home

See it

Real screenshots of a running meerpic, filled with CC0 demo photos rather than anyone's library. Click any of them to see it full size.

The grid
Search
Similar
The map
A photo
Text
Albums
Delete

Get started

There is no installer yet: for now it is a checkout and make up. You need Docker, your photos in one or more folders, and room for the cache.

What you need

The agent image brings its own exiftool, a full ffmpeg and rclone; from your machine it takes your photo folders, and for iCloud your rclone.conf. The first index is the expensive part. After that, only new photos cost anything.

DockerEngine 24+ with the Compose v2 plugin. Runs Postgres, the server and the agent.
DiskAbout 40 KB per photo for thumbnails, 5 to 10% of your videos' size for the playable copies, and 1.5 GB for the search model.
CPUAbout two hours for 25,000 photos on a 20-core laptop, then the video copies in the background.
rcloneOnly for iCloud Photos: 1.74+ with an iclouddrive remote using service = photos.

1. Clone it and start it

Postgres, the server and the agent, in Docker, on http://127.0.0.1:8040. There is no password by default, which is right for 127.0.0.1: set one before you expose the port.

git clone https://github.com/ribalba/meerpic
cd meerpic
make up

2. Tell it where your photos are

The first make up writes meerpic.toml. [library] roots there lists the folders to index, as many as you like, each under a name; the default is ~/Pictures/iCloud. Folders outside ~/Pictures also need MEERPIC_PICTURES in .env.

# meerpic.toml
[library]
roots.icloud = "~/Pictures/iCloud"
roots.camera = "~/Pictures/Camera"

3. Let it index

The agent starts at once, newest first: the last few days are browsable within a minute, searchable a little after, and the rest fills in behind. Videos get their playable copies once everything has a thumbnail and a search vector, except one you open, which is converted right away. The sidebar shows what is left.

make agent-test
make logs

Optional: iCloud Photos

rclone config: a new remote of type iclouddrive, your Apple ID, and service = photos. Then Sync copies the library into ~/Pictures/iCloud. When Apple wants a new two-factor code, every few weeks, the sync says so: run rclone config reconnect iclouddrive: and press Sync again.

rclone config
make sync

Or run it as a desktop app

A thin Electron window around the server, like meercal's and meerail's, with one thing a browser cannot do: dragging a photo out hands the target a real file, so a mail gets an attachment rather than a link. It needs the server running.

make desktop

meerpic is at 0.1.0. Not there yet: an installer, and editing, which is on purpose: the files are the truth, and meerpic never writes one.