# photoview: a photo gallery that treats your filesystem as the album structure

> A Go and React self-hosted gallery that maps directories to albums, parses EXIF and RAW, detects faces, and has spent two years on master without cutting a release.

**photoview/photoview** — Photo gallery for self-hosted personal servers

- Repository: https://github.com/photoview/photoview
- Website: https://photoview.github.io/
- Stars: 6,557 · Forks: 491
- Language: Go
- License: AGPL-3.0
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/photoview-photoview

## The filesystem is the database, in the sense that it is the schema

Photoview does not ask you to import anything. You tell it which directory on the server to look at, and it presents what it finds. The README states the arrangement as the first main feature: the website presents the images found on the local filesystem of the server, and directories are mapped to albums.

That single choice explains most of the rest. There is no separate album object to create, no upload endpoint, no import wizard. A scanner picks up new media, generates thumbnails, and the media appear on the site organised in the same way as on disk. Reorganising your photo folders reorganises the gallery, which is convenient right up until the moment you want a logical view that does not correspond to a real directory.

User management follows the same logic with a twist. Each user is created together with a path on the local filesystem, and photos within that path are accessible to that user. So access control is expressed as a directory boundary rather than as a permission table, which means a mistake in the path configuration grants or denies exactly what you would expect and nothing more subtle than that.

The repository layout backs this up. There is an api directory and a ui directory, plus dependencies, scripts, systemd, a development compose file and a directory of compose examples. The project is written in Go with a React frontend, and the two are versioned together in a single repository rather than split into separate components with independent release cycles.

## Two migrations are the first two sections of the README

Before any feature description, the README puts two notices in all caps, and both will bite an upgrade.

The first is a Docker registry migration. Images moved from the viktorstrate/photoview registry to photoview/photoview, and all new master images and future releases go to the new one. Old images stay available in the old registry, so an existing deployment that keeps pulling from viktorstrate/photoview will keep running an old image indefinitely while the repository looks busy. The README asks you to update the photoview image reference in your compose file and points at the example compose file in the repository for the exact form.

The second is a database migration. The master branch moved to PostgreSQL 18, which is the recommended default for the next release, with PostgreSQL 17 still supported. For Docker users there is a concrete filesystem consequence: from version 18 the database is mounted at /var/lib/postgresql, where earlier versions used /var/lib/postgresql/data. Miss that and you will come back to an empty database after a recreate, which is the kind of surprise that turns a two-minute upgrade into an afternoon.

Both notices point at the same example file, docker-compose.example.yml, which is the most reliable single artefact in the repository for a current deployment. The README's advice on the database is worth repeating verbatim in spirit: back up before upgrading.

## The feature list is aimed squarely at photographers

RAW and EXIF parsing head the list of reasons to use this over a generic file browser. Video support covers common formats and optimizes them for the web automatically. Face detection groups photos of the same person together, which is the feature that most often decides whether a gallery feels like a photo application or like a directory listing.

Performance is handled by deferring work to visibility. Thumbnails are generated automatically, photos load when they become visible on screen, and in full screen the thumbnail stays up until the high resolution image has finished loading. On a library of high resolution photos that is the difference between a usable timeline and a browser that appears to hang.

Security is described in three concrete claims rather than adjectives: all media resources are protected with a cookie token, all passwords are properly hashed, and the API uses a strict CORS policy. Those are checkable statements, and the first one matters most in practice, since without it a shared link would expose every original file to anyone who could guess a path.

Sharing sits alongside this: albums and individual media can be shared by public link, with optional password protection. Given that originals sit on a filesystem you control, that feature is what lets a family member see a holiday album without giving them a shell.

## A multi-stage Dockerfile that names its own platform boundaries

The Dockerfile is more informative than the README's install section, and it reads like a description of the project's constraints. The user interface is built on Node 18 with npm pinned to major version 10, which the file comments on as the latest supported npm for that Node line:

```dockerfile
FROM photoview/dependencies:trixie AS dependencies

SHELL ["/bin/bash", "-euo", "pipefail", "-c"]

WORKDIR /dependencies
RUN tar xfv /artifacts.tar.gz
```

The UI stage sets REACT_APP_API_ENDPOINT and UI_PUBLIC_URL as build arguments rather than runtime environment variables, because they are baked into the JavaScript bundle at build time. The API stage builds from golang:1.27-trixie. Both the dependency artifact stage and the API stage use Debian trixie, so the three build stages agree on a base distribution.

That consistency is not cosmetic. The dependencies stage unpacks a prebuilt artifact tarball rather than compiling libraries inside the image, which is how the project keeps ffmpeg and the native imaging libraries out of the build path and ships them as a separate image. Anyone building from source needs to build or pull that dependencies image first, and the repository tree confirms it by carrying a dependencies directory alongside api and ui.

The repository also ships a .hadolint.yaml, so the Dockerfile is linted in CI, and a dockerignore and dockleignore to keep the build context and the image scan narrow.

## Supported platforms, and the one abstraction layer that is excluded

The platform list is long, and it is worth reading for what it excludes. Docker is first, with a stated floor: a Docker Engine released within the last year and a major version not older than the previous one. Then Debian 12 and 13, Ubuntu LTS and newer short-term releases, Arch Linux through the AUR, NixOS, Unraid, EmbassyOS, YunoHost and TrueNAS.

The exclusion is Portainer and similar abstraction layers built on top of Docker Compose, and the reason given is about debugging rather than policy. The project asks you to reproduce any problem on plain Docker Compose before reporting it, because a wrapper that rewrites environment variables or networking produces failures the project cannot see. That is a reasonable request and a common one, but it does mean a Portainer user is on their own with a configuration the maintainers will ask them to abandon.

The README also states plainly that support for the standard deployment scenarios on these platforms is limited, and that the team has no testing for some of them. NixOS, Unraid and TrueNAS entries therefore signal community packages rather than a supported configuration.

Two other prerequisites appear in the README's table of contents without being described on its front page: hardware acceleration, and an initial setup step. Those live further down the page, which is a reminder that the front-page feature list is the marketing half of the documentation and the compose example is the operational half.

## The last release is two years behind the last commit

The release history is short and the gap is large. Version 2.4.0 was published on 2024-06-23 under the title announcing a project restart with a lot of improvements. Before that, version 2.3.13 dates from 2022-07-26 and is described as internal restructuring and improvements, with a Basque translation added and a slow query in the face media lookup fixed.

The last push to the default branch, master, was on 2026-09-21. So master has more than two years of commits that have never been packaged into a tagged release, and the PostgreSQL 18 migration described in the README is one of the changes on that branch rather than something you get from a 2.4.0 image.

That changes how you deploy. Pulling the newest release tag gets you software from mid-2024 with the old database layout and the old registry path. Following master gets you current code with the new layout, and requires the compose changes the README spells out. There is no release that combines both, and the 189 open issues are where the friction of that arrangement shows up.

The 2.4.0 release notes are unusually emphatic about this, warning Docker users that simply pulling the new image will not work if you have not already moved to the master branch and the new configuration. The project restarted once and the restart is still in progress, two years on.

## A demo instance exists, with credentials in the README

The README links a public demo at photos.qpqp.dk and prints the username demo and the password demo next to it. For a project whose selling point is that it holds a real photo library, this is the fastest way to form a view of the interface without installing anything, and the decision to publish the credentials openly signals that the instance is intended to be poked at rather than protected.

What the demo cannot tell you is how the scanner behaves on a large library, because that is a function of your filesystem and your disk, not of the application. Thumbnail generation is the part that will take real time on first scan, and the README's claims about loading behaviour only apply once that first pass has finished.

The documentation site is photoview.github.io, which is also the homepage recorded in the repository metadata. The README itself defers development questions to a Discord server, and the repository tree includes a CONTRIBUTING file and a scripts directory, so the contribution path is conventional even though the packaging path is currently unusual.

## Conclusion

Photoview's design decision is to have no import step. Directories on your server are the albums, and everything else in the feature list is derived from that. The cost is that it is a server application and not a desktop tool, and the cost of following master is that you are running unreleased code against a database version it changed under you. If you want a gallery that mirrors a disk you already have, this fits. If you want tagging, ratings and albums as first-class objects rather than as directories, you will be disappointed by the gap between what the UI shows and what the filesystem stores. Read the two ATTENTION blocks at the top of the README before anything else, then check whether a tagged release has appeared since 2.4.0.

## FAQ

### What is photoview?

photoview is a self-hosted photo gallery for a personal server, written in Go with a React frontend and licensed AGPL-3.0. You point it at a directory on the server and it maps directories to albums, scans the media, and generates thumbnails.

### Does photoview require importing photos into a database?

No. The README states that Photoview is closely tied to the file system, that the website presents images found on the local filesystem of the server, and that directories are mapped to albums. A scanner picks up new media automatically.

### Which PostgreSQL version does photoview need?

The master branch has switched to PostgreSQL 18, which is the recommended default for the next release, while PostgreSQL 17 remains supported. Docker users on version 18 must also move the database volume mount point to /var/lib/postgresql instead of /var/lib/postgresql/data.

### Which Docker image should I pull for photoview?

Use photoview/photoview from Docker Hub. Images moved there from viktorstrate/photoview, and the old registry still serves the older images, so a deployment that keeps the old reference keeps running old code even though the repository has moved on.

## Sources

- [License: AGPL-3.0](https://github.com/photoview/photoview/blob/master/LICENSE)
- [photoview/photoview on GitHub](https://github.com/photoview/photoview)
- [Project website](https://photoview.github.io/)
- [README](https://github.com/photoview/photoview/blob/master/README.md)
- [Releases](https://github.com/photoview/photoview/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/photoview-photoview
