Home Gallery serves one user, and loads the whole index into the browser
Self-hosted open-source web gallery to view your photos and videos featuring mobile-friendly, tagging and AI powered image discovery
At a glance
- What is it?
- Home Gallery is a self-hosted photo and video gallery that identifies media by content, keeps read-only and offline disks usable after a single extraction pass, and finds forgotten photos by reverse image lookup and face similarity. It is a single-binary Node application with an explicit ceiling: the entire index is shipped to the browser, and the page quantifies exactly how large that gets.
- Who is it for?
- Home Gallery fits one household or one person with a large archive on a NAS, who wants phone-quality browsing and search without uploading anything.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 100 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One user only, and every file is served
The target user list is the architecture. It names computer affine users who solve their own problems, people serving local data without cloud services, and one line that settles the rest: one user only, all files are served.
There are no per-user permissions because there is one user, and the feature list has no sharing, no accounts and no access control. What replaces those is content. The motivation section is equally specific: the source of the archive is a NAS at home, cloud services do not cover the privacy concern, existing gallery software was slow on phones, and the point is to browse and rediscover forgotten memories from the complete archive.
That is a narrower product than a photo service and a more honest one. A single binary serves the whole library, and the application is expected to be maintained by the person running it, which is why the page describes itself as a private pet project with no warranty and points at a forum, a Discord server and two donation links rather than a support policy.
The interface side is mobile-first for a reason: endless photo stream with virtual scrolling, a progressive web app for phones, single and multi selection tagging, and an expressive query language with and, or and not operands.
The whole index travels to the browser, and the page measures it
There is a limits section, and it is more useful than most projects' limits sections because it contains numbers:
> The complete "database" is loaded into the browser. My 100.000 media are about 100 MB plain JSON and 12 MB compressed JSON.
One hundred thousand files becomes a hundred megabytes of JSON before compression and twelve after, and the page states the performance is quite good on current mobile devices. It also records that a user reported a successful setup with over 400,000 media files, and asks for more feedback.
So the ceiling is real but not razor thin, and the design that produces it is the same one that makes the mobile experience fast: no query round trip to page through an endless stream, because the index is already there.
The compose file carries the matching knob. A commented `NODE_OPTIONS=--max-old-space-size=4096` line is labelled for larger galleries to increase the memory limit, which is the point at which the server side, not the browser, starts to matter.
Other export paths exist for the same reason: a static web gallery site export, which is what the public demo gallery is, and metadata export to XMP sidecar files for people who want their tags in a desktop tool.
Media are identified by content, and offline disks stay offline
Two related mechanisms decide what gets processed and what gets touched again.
Media are identified by their content, so files that are identical byte for byte are only processed once, and renaming is supported without recalculating previews or metadata. That is what makes a large archive cheap to reorganise: the expensive work is keyed to the bytes, not to the path.
Change detection is then layered on top, described as fast detection of adds, removals, renames and moves. Content identity plus path-change detection is what lets you reorganise a decade of folders from a desktop, on a NAS, without triggering a re-index.
The second mechanism is about disconnection. Read-only and offline media sources are supported, and the explanation is specific: once preview files are generated and metadata extracted, the original sources are not touched or required any more, so media on an offline disk need to be extracted only once and the disk can stay offline on subsequent runs.
That is the feature that makes cold storage practical. A shelf of archived drives does not have to be mounted for the gallery to work, only for the first extraction pass.
Discovery runs on your machine, and one public service is named
The discovery features are the reason the project calls itself AI powered, and three of them are about finding things you did not tag.
Reverse image lookup, or similar image search, is described plainly: given one sunset image you can find the other sunset photos in your archive without manual tagging. Face detection and search by similar faces does the same for people. GEO location reverse lookups turn coordinates into a place.
The privacy section states the goal as using as few public services as possible because the image data is sensitive private material, and the approach as preferring services which can be deployed locally. It also concedes the cost in the same paragraph: the setup requires technical knowledge and technical maintenance.
The enumeration of services then begins and names one, Nominatim, for geo reverse lookups from coordinates to an address. Face detection and image similarity are the features that run without leaving the machine, which is why they can claim that.
The inference side of that is visible in the compose file, where the API service selects a TensorFlowJS backend with the alternatives commented and annotated: cpu described as the slowest with the best support, wasm as good performance on arm64 and amd64, and node as the best performance on amd64. The shipped choice is wasm.
Change detection polls every 300 seconds, and SoC users get three other numbers
The container sets a watcher interval, and the comment explains why the default is not the smarter option:
> Use polling for safety of possible network mounts. Try 0 to use inotify via fs.watch
Both the image and the compose file set `GALLERY_WATCH_POLL_INTERVAL=300`. A NAS over NFS or SMB may not deliver filesystem events reliably, so the safe default polls every five minutes and the faster path is opt-in by setting the value to zero.
The same section of the compose file is tuned for small computers, with comments naming the trade-offs. `GALLERY_API_SERVER_CONCURRENT=1` and `GALLERY_API_SERVER_TIMEOUT=60` are both marked for SoC devices like the Raspberry Pi, with the guidance to use 5 and 30 otherwise. That is a two-orders-of-magnitude difference in concurrency for the same application, decided by what hardware you have.
The page also claims it runs on SoCs such as the Raspberry Pi, and the project ships binaries for Linux, macOS and Windows. So the low-power path is a stated target rather than an accident.
The install is a curl into an executable, with no checksum shown
The quickstart is four commands:
curl -sL https://dl.home-gallery.org/dist/latest/home-gallery-latest-linux-x64 -o gallery
chmod 755 gallery
./gallery init --source ~/Pictures
./gallery run serverDownload, make executable, initialise with a media source, run the server on localhost:3000. The CLI has its own help output behind `./gallery -h`, and the configuration lives in `gallery.config.yml` in the current directory, with a schema and an example file at the repository root.
The Docker version is the same two commands behind an alias, which is a neat way to keep one mental model:
alias gallery="docker run -ti --rm \
-v $(pwd)/data:/data \
-v $HOME/Pictures:/data/Pictures \
-u $(id -u):$(id -g) \
-p 3000:3000 xemle/home-gallery"Running as your own uid and gid keeps the generated database and previews owned by you rather than by root, and the configuration moves to `./data/config/gallery.config.yml`.
What the quickstart does not show is a checksum. A `curl` of an executable straight into a path you then `chmod 755` is the shape of a supply-chain risk, and this project is explicitly about not handing your photos to anyone. The download directory is linked for further binaries, and verifying against it is your move, not the page's.
The image drops its native resizer on 32-bit ARM
The Docker build is two stages on an Alpine Node base, and the interesting part is a conditional: the build always disables one dependency for the API server, and disables `sharp` in the extractor package when a build flag is set or when the target platform is `linux/arm/v6` or `linux/arm/v7`.
`sharp` is the native image resizing library. Dropping it on 32-bit ARM is a pragmatic call, and the cost is that those builds fall back to a different resizer. The runtime side shows the same concern from the other direction: `GALLERY_USE_NATIVE` is set to the ffprobe and ffmpeg binaries, with a commented line adding vipsthumbnail and a note to use it on issues with the sharp resizer. The final image installs ffmpeg, vips-tools and perl for that reason, so the transcoding and thumbnailing tools are present whether or not the native module is.
Two documentation details are inconsistent in a small way. The image labels point documentation at `docs.home-gallery.org`, while the page's documentation links go to `home-gallery.org/docs`. Both resolve in practice, and anyone reading the label in a registry listing will be sent to a different host than the one in the documentation index.
The image also sets `HOME=/data` and a volume at `/data`, so the data directory is simultaneously the working directory and the home directory, which is how a container can be handed a bare volume and still work.
pnpm workspaces in the repository, npm inside the image
The manifest is a monorepo root named `@home-gallery/mono-repo` at version 1.21.0, ESM, exporting a single entry point and registering a `gallery` binary that points at the same file. Workspaces are declared as `./packages/*` and a `pnpm-workspace.yaml` sits at the root.
The scripts then mix tools. `clean`, `build` and `test` are `pnpm -r` calls across the workspace, while `postinstall` shells out to `npm --prefix e2e install`, and the configuration build chains npm-run-all steps. The end-to-end suite is Gauge, invoked as `gauge -d e2e run specs`, with the Gauge CLI in the development dependencies.
The container build does not use the workspace tool the repository declares: it runs `npm install --no-audit` and then `npm run build`. So the image is built through a different package manager than the one the scripts assume, which works and is not what most maintainers would choose deliberately.
Maintenance is quiet rather than dormant. There are no GitHub releases, and the changelog lives in the repository as a file. The last push is dated 2026-06-25, the default branch is master, the project is MIT licensed, and continuous integration appears to run on Drone, given a `.drone.yml` alongside the GitHub directory. The page asks for contributions through CONTRIBUTING.md and names the maintainer's Patreon and PayPal for recurring or one-time support.
Editorial conclusion
Home Gallery fits one household or one person with a large archive on a NAS, who wants phone-quality browsing and search without uploading anything. Verify four things first: that one user is enough, because the target audience is stated as one user with all files served; how big your archive is, since the index is loaded into the browser and the page quantifies a hundred thousand files; whether the geo lookups are acceptable, since the Nominatim service is named as the one external call; and which package manager your build uses, since the repository declares pnpm workspaces while the container image is built with npm. MIT licensed, version 1.21.0, with the last push dated 2026-06-25 and no GitHub releases.
Frequently asked questions
What is Home Gallery?
It is a self-hosted, MIT licensed web gallery for browsing personal photos and videos, with tagging, mobile-friendly browsing through a progressive web app, and image and face discovery. It runs as a single binary or a Docker image on localhost:3000 and identifies media by content so renames do not force a re-index.
How do I install Home Gallery?
Download the Linux binary with curl, chmod 755 it, then run `./gallery init --source ~/Pictures` and `./gallery run server`, and open localhost:3000. The Docker quickstart defines an alias so the same two commands run inside a container with your media directory mounted under /data and your own uid and gid.
How many photos can Home Gallery handle?
The complete index is loaded into the browser: 100,000 media are about 100 MB plain JSON and 12 MB compressed JSON, and one user reported a successful setup with over 400,000 files. The compose file carries a commented NODE_OPTIONS memory-limit option for larger galleries.
Does Home Gallery work with an offline disk?
Yes, read-only and offline media sources are supported. Once preview files are generated and metadata extracted, the original sources are not touched or required again, so media on an offline disk only need to be attached for the first extraction pass. For geo reverse lookups the page names the Nominatim service as the external call.
Does Home Gallery support more than one user?
No. The stated target is one user only, with all files served, so there is no account system and no per-user filtering. It is built for a single household archive rather than a shared or multi-tenant library.
How does Home Gallery detect new and changed files?
Media are identified by content, so byte-identical duplicates are processed once and renames do not require recalculating previews, and changes such as adds, removals, renames and moves are detected quickly. Change detection itself polls: GALLERY_WATCH_POLL_INTERVAL is 300 seconds by default, and the note says to try 0 to use inotify via fs.watch instead.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/xemle-home-gallery)