Self-hosted service
SmilyOrg/photofield avatar
SmilyOrg/photofield

Photofield: a single-binary, read-only photo gallery built for zoom speed

A self-hosted non-invasive single-binary photo gallery with a focus on speed and simplicity.

608 stars16 forksGoMIT

At a glance

What is it?
Photofield is a self-hosted Go gallery that treats your filesystem as the source of truth and keeps everything else in a disposable cache. It is fast at laying out tens of thousands of images, and it is explicitly not built for many simultaneous users.
Who is it for?
Adopt Photofield if you have a large local photo or video collection on a fast disk, you want a read-only viewer that never rewrites your originals, and you are the only person or one of a very small number of people browsing at a time. Do not adopt it if you need per-user logins, shared albums with permissions, or on-the-fly video transcoding; the README states there is no authentication or authorization support and that transcoding is not supported.
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 46 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem Photofield picks: showing 43k images at once without touching them

Most self-hosted galleries ask you to import. They copy files into a library, write sidecar metadata, and from then on the database is the truth. Photofield inverts that. The README states the original files are not touched, and that you are encouraged to mount your photos read-only, because "the file system is the source of truth, everything else is just a more or less stale cache." That single decision explains most of the rest of the project: indexing can be thrown away and rebuilt, and there is no migration step to plan.

The audience is narrower than the feature list suggests. The README frames the goal as pushing "the limits of what is possible in terms of the number of photos visible at the same time and at the speed at which they are displayed," aiming to match or beat Google Photos on commodity hardware while showing more images at once. The demo GIF in the README shows a zoom from a wall of images down to a logo inside a sample of 43k photos from the open-images-dataset, captured on an i7-5820K with an NVMe SSD. That is the target user: someone with a big local archive and a fast disk who wants browsing to feel immediate, not someone running a shared family server.

Server-side layout, tiled rendering, and a cache you can delete

The architecture is split between a Go server that owns indexing and tile rendering and a Vue 3 front end that draws the result. The go.mod file lists OpenLayers among the front-end dependencies, and the README describes "in-browser tiled image rendering" via OpenLayers, with the Go side doing "API and server-side tile rendering" through the tdewolff/canvas library. So the browser is not handed a folder listing and left to arrange it. The server computes the layout and serves tiles.

That choice has a visible cost. The README's limitations section says a lot of normally client-side state is kept on the server, and warns you will likely hit CPU or memory problems with more than a few simultaneous users. It also notes the initial load can be slow, because all photos need to be laid out for a specific window size and configuration the first time you load a page, which can take time on a slow CPU with a cold HDD cache.

Indexing is staged. File walking uses godirwalk and the README claims 1000 to 10000 files per second on a fast SSD with a hot cache. EXIF metadata and prominent color extraction run as separate follow-up operations, at up to roughly 200 files per second and 1000 files per second respectively on a fast system. Thumbnails live in SQLite, with FFmpeg handling on-the-fly format conversion, embedded thumbnails pulled from JPEGs, re-use of Synology Moments or Photo Station thumbnails, and djpeg from libjpeg-turbo used to decode lower resolutions efficiently. Reverse geolocation is embedded locally via tinygpkg, covering around 50 thousand places, and works in the Timeline and Flex layouts.

Installing Photofield and pointing it at a photo directory

The README points to a Quick Start page at photofield.dev/quick-start and says Docker images are available alongside the single static binary. The repository's Dockerfile builds the binary with the embedui, embeddocs and embedgeo tags, then runs it on Alpine 3.24 with exiftool, ffmpeg, libjpeg-turbo-utils and libwebp installed, exposing port 8080 and setting PHOTOFIELD_DATA_DIR to /app/data. The working directory in the runtime stage is /app/photos, which is where the entrypoint starts.

The repository's docker-compose.yaml shows the shape of a local run. It builds from the repository root, names the image photofield, maps 8080 to 8080, and mounts a host directory read-only into the container:

yaml
services:

  photofield:
    build: ./
    image: photofield
    ports:
      - 8080:8080
    volumes:
      - ./docs/assets:/app/assets:ro
    restart: "no"

Swap ./docs/assets for your own photo directory and keep the :ro suffix, since the read-only mount is the mechanism that enforces the non-invasive promise. After bringing that service up, the gallery is served on port 8080. The first page load triggers the layout pass the README warns about, so a large collection will not be instant on a cold cache.

For a direct binary run, the Dockerfile shows the only environment variable the image sets, and the same variable applies outside Docker:

bash
export PHOTOFIELD_DATA_DIR=/app/data

The data directory holds the SQLite cache and a configuration.yaml, which the Dockerfile creates empty at build time. Deleting that directory costs you thumbnails, tags and cached metadata, not your photos.

Where Photofield is the wrong tool

The limitations list is unusually blunt, and three entries matter more than the rest. First, no user accounts. The README says authentication and authorization are not supported, and suggests defining separate collections per user through directory structure instead. If your gallery needs a login page, Photofield does not have one, and putting it behind a reverse proxy is a decision you make outside the project.

Second, concurrency. Because layout state lives on the server, the README warns of CPU or memory problems with more than a few simultaneous users. A household where two people browse occasionally is a different load profile from a club sharing an archive.

Third, no permalinks. Deep links to images work, but the README states that if you remove the database or move files around, those links may break. Combined with the cache-is-disposable model, this means a rebuild can invalidate links you have shared. Video is also partial: multiple resolutions are supported if something else pre-generated them, such as Synology Moments, but on-the-fly transcoding is not supported. If your library is mostly phone video in HEVC, check the FFmpeg build before assuming playback.

Photofield against PhotoPrism and Damselfly: import versus index in place

The closest comparisons people search for are PhotoPrism and Damselfly, and the difference is architectural rather than cosmetic. PhotoPrism organizes an originals-plus-sidecar library and runs its own indexing and machine-learning pipeline over it; the library is a managed store that the application owns. Damselfly similarly maintains a catalog and adds its own search and face features on top.

Photofield does neither. It reads a directory tree, writes nothing back, and keeps a cache it is happy to lose. That is why the README can describe it as usable "either completely standalone or complementing other photo gallery software": you can point it at the same Synology Moments directory another tool manages and it will re-use the existing thumbnails. The trade is that features which require durable per-photo state are marked alpha. Tagging stores tags in the cache database, and face detection depends on the separate photofield-ai project. Semantic search is also gated behind photofield-ai. If you want those capabilities to survive a cache wipe without re-running a model, a tool whose database is the source of truth fits better.

Maintenance, releases, and what the MIT licence leaves you to handle

The repository is not archived, and the last push was on 2026-08-17, the same day v0.25.0 was released. The release history shows a steady cadence: v0.24.0 in June 2026 added faces, filename search and layout improvements, v0.24.1 in the same month was labelled security patches, and v0.25.0 in August 2026 covered an MCP server and stability improvements. The go.mod pins Go 1.25.11 with a toolchain of go1.25.12, so building from source expects a recent Go toolchain. The repository also carries a .changie.yaml and a .changes directory, which suggests changelog entries are collected per change rather than written by hand at release time.

Upgrade cost is low in the normal case. Because the filesystem is the source of truth and the cache is disposable, a new binary can be dropped in and the cache rebuilt. The real cost is time: re-extracting EXIF and prominent color runs at the rates the README quotes, so a rebuild of a large library is measured in hours, not minutes. Photofield is MIT licensed, which is permissive and places few obligations on how you run or redistribute it. Note that the optional photofield-ai component for semantic search and face detection is a separate project with its own licence, and the Dockerfile pulls in FFmpeg and exiftool from Alpine packages under their own terms. That is a description of what the repository states, not legal advice.

Editorial conclusion

Adopt Photofield if you have a large local photo or video collection on a fast disk, you want a read-only viewer that never rewrites your originals, and you are the only person or one of a very small number of people browsing at a time. Do not adopt it if you need per-user logins, shared albums with permissions, or on-the-fly video transcoding; the README states there is no authentication or authorization support and that transcoding is not supported. Before committing, verify two things on your own data: that the initial layout pass on a cold cache finishes in a time you can tolerate on your hardware, and that the FFmpeg build you install carries an HEVC/H.265 decoder plus libheif, because the Dockerfile notes v7.0 or newer is recommended for HEIC and MOV support.

Frequently asked questions

Does Photofield modify or move my original photo files?

No. The README states the original files are not touched and that you are encouraged to mount your photos as read-only, because the file system is the source of truth and everything else is a more or less stale cache.

Can several people use one Photofield instance at the same time?

The README lists "Not optimized for many clients" as a limitation, noting that much of the normally client-side state lives on the server and that more than a few simultaneous users will likely cause CPU or memory problems.

Does Photofield have user accounts or login?

No. The README says there is no authentication or authorization support, and suggests defining separate collections for separate users based on directory structure instead.

Can Photofield transcode videos on the fly?

No. The README states that videos are supported along with multiple resolutions if they were pre-generated, for example by Synology Moments, but that on-the-fly transcoding is not supported.

What port does Photofield listen on by default?

The Dockerfile exposes port 8080 and sets PHOTOFIELD_DATA_DIR to /app/data, and the repository's docker-compose.yaml maps 8080 to 8080 for the photofield service.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. SmilyOrg/photofield on GitHub
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/smilyorg-photofield.svg)](https://hysenlabs.com/projects/smilyorg-photofield)