# Visual Regression Tracker: a self hosted service that compares screenshots for you

> Visual Regression Tracker is a self hosted service that accepts screenshots from any automation framework, compares them pixel by pixel against accepted baselines, and keeps the history of how each baseline changed. It suits teams whose visual tests need somewhere to live, and it is not an assertion helper you can call from a single test file.

**Visual-Regression-Tracker/Visual-Regression-Tracker** — Backend and Frontend application for tracking differences via image comparison

- Repository: https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker
- Website: https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker
- Stars: 722 · Forks: 66
- Language: Shell
- License: Apache-2.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/visual-regression-tracker-visual-regression-tracker

## The service compares images; it does not capture them

The mechanism is one sentence in the readme: the service receives images, performs a pixel by pixel comparison against its previously accepted baseline, and provides immediate results in order to catch unexpected changes.

Everything about that sentence defines the scope. The service never takes a screenshot. It never drives a browser. It has no opinion about your test framework, and the feature list states that directly as automation framework independence, meaning there is no need to stick with a specific automation tool and it integrates with an existing one. The same applies to platforms: web, mobile or desktop, as long as you can make a screenshot.

The integration surface is therefore a REST API. The readme lists language support as JS, Java, Python, .Net or any other via REST API. There is no SDK layer described, so an integration means an HTTP POST of an image plus the metadata that identifies which test variation it belongs to.

This split is the design decision that makes the project useful and also the one that limits it. Because the service owns only the comparison and the history, it can serve a team that runs Cypress in one repo, Playwright in another and a mobile pipeline in a third, and still keep every result in one place. The cost is that the capture side, including the retries, the viewport matrix and the flakiness handling, stays in your code.

The repository layout matches the split. The public/ directory holds the UI assets, and the docker-compose.yml wires together a ui service, an api service, and a Postgres service. The api is a Node service on port 3000 behind the ui on port 8080.

## The TestVariation, Baseline, TestRun and Build data model

The glossary in the readme defines five terms, and they are the vocabulary of the API.

A TestVariation is the historical record of baselines identified by name plus branch plus OS plus browser plus viewport plus device. A Baseline is a validated and accepted image, and the latest one is what a TestRun compares against. A TestRun is the result of comparing an image against a baseline. A Build is a list of TestRuns. A Project is a list of Builds and TestVariations.

That model is the interesting part of the design, and it has consequences worth spelling out. Because a variation is keyed by six dimensions, the same screenshot name in the same branch on a different operating system or at a different viewport is a different variation with its own baseline. That is what makes per-platform baselines possible, and it is also what makes the baseline count grow quickly, since every browser and device combination multiplies the stored images.

A Build is a grouping of runs, which is the unit you would map onto a CI build. A Project then aggregates builds and variations, so the whole history of a screen across branches and platforms lives under one project.

The feature list adds baseline history, described as tracking how the baseline image changed since the beginning, so a reviewer can see not just that a screen differs today but how the approved image evolved over time. That is the part that a simple assertion cannot give you.

For a team writing an integration, these five nouns are the contract. If you get the variation dimensions wrong, the service will treat your screenshots as belonging to different tests, which is the kind of mistake that shows up as a wall of false positives rather than an error.

## Four comparison providers and a per project choice

The comparison algorithm is configurable per project, in the project settings, and four providers are listed.

Pixelmatch is the default, described as a fast pixel by pixel comparison algorithm and best for detecting exact pixel differences. Looks-Same is a perceptual comparison that considers human visual perception, and is described as good for ignoring minor rendering differences. Odiff is an advanced diff tool with layout detection and anti-aliasing support, useful for detecting layout shifts.

The fourth is the one that stands out. The VLM provider is a hybrid comparison that combines pixelmatch with AI powered human-noticeability analysis using Vision Language Models, and it analyses whether differences are actually noticeable to humans. The readme points to a separate VLM README in the backend repository for the details.

That option is the interesting engineering bet of the project. A pure pixel diff flags every anti-aliased edge and every subpixel text rendering difference, which is why teams end up writing ignore regions. A perceptual or VLM approach tries to answer a different question: would a person looking at both images notice anything. The api service environment in the compose file includes an OLLAMA_BASE_URL variable, which is consistent with running a local model for that analysis.

The practical advice is to treat the provider as a per project setting rather than a global one, which is what the documentation specifies. A dashboard with text and thin borders wants perceptual comparison, a pixel art game wants Pixelmatch, and a layout heavy page may want Odiff. The cost of the VLM provider is that every comparison becomes a model inference, and the readme does not document the latency or the operational requirements of running that model.

## Install with the script, or copy the compose file and the env file

Docker has to be installed on the machine, and the readme offers two routes.

The quick setup downloads an installation script and makes it executable:

```bash
curl https://raw.githubusercontent.com/Visual-Regression-Tracker/Visual-Regression-Tracker/master/vrt-install.sh -o vrt-install.sh
chmod a+x vrt-install.sh
```

Running the script then installs the tracker. The script takes a small set of arguments, printed by its own help output, and the defaults are the front end on http://localhost:8080 and the API on http://localhost:4200. The JWT secret defaults to a randomly generated value, which is the right default for a quick start and something you should set explicitly for a real deployment.

The manual setup copies docker-compose.yml and .env from the repository, edits REACT_APP_API_URL in the .env file so it points at the right address, changing localhost to an IP or another resolvable name if the instance will be reached from other machines, makes sure the ports are free, and then starts the service with docker-compose up. You then wait until the credentials are printed, and new users and projects can be created through the front end at http://localhost:8080.

The compose file shows the shape of the deployment. The ui service runs the visualregressiontracker/ui image, publishes the port to 8080, mounts ./imageUploads into the nginx static directory, and receives REACT_APP_API_URL, VRT_VERSION and PORT as environment variables, with restart always set. The api service runs visualregressiontracker/api, connects to Postgres through a DATABASE_URL built from the POSTGRES variables, and takes a long list of configuration including the AWS credentials and bucket for S3, the JWT secret and lifetime, the front end URL, a body parser JSON limit, the Elasticsearch URL, a full set of LDAP variables and the OLLAMA_BASE_URL. The service is published on APP_PORT mapped to 3000, with host.docker.in added to extra_hosts so container to host calls work.

## LDAP, S3 and the Elasticsearch logging variant

Three optional pieces of the deployment deserve a note.

LDAP is fully wired into the api service environment, with LDAP_ENABLED, LDAP_TLS_NO_VERIFY, LDAP_URL, LDAP_BIND_USER, LDAP_BIND_PASSWORD, LDAP_SEARCH_DN, a users search filter, and separate attributes for last name, first name and mail. That is enough to authenticate against a directory server, which matters for an internal tool, and the readme does not document the search filter syntax, so you will need to consult the backend code or your directory's schema for that.

Storage is either local or S3. The imageUploads volume mounted into the ui service is the local path, and the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION and AWS_S3_BUCKET_NAME variables alongside the STATIC_SERVICE variable let the api write to S3 instead. For a self hosted tool, the local volume is the default and the S3 path is what you would use when the api runs somewhere without persistent disk.

The Elasticsearch variant is documented separately and is aimed at administrators who already know the Elastic stack, particularly Kibana. The readme is candid about the cost, noting that retaining logs in Elasticsearch consumes a little more memory and CPU. Two compose files are involved: docker-compose.elastic.logging.yml when you start your own Elastic stack, and docker-compose.logging.yml when you point the filebeat configuration at an existing Elasticsearch server, which also means setting ELASTIC_URL in the .env file. There is a filebeat config file under filebeat/config/filebeat.yml, and a known permissions error if you are not running as root, since the filebeat configuration file must be owned by uid 0. The readme also recommends running as root to avoid ownership issues, which is a reasonable default for a self hosted tool on a workstation but not something to carry into a production container policy.

## Where Visual Regression Tracker is the wrong choice

Several cases argue against it.

The first is a single test suite with a handful of screenshots. Setting up a service, a database, an API and a UI to compare twenty images is a lot of infrastructure. Most projects that want visual assertions already have them, in whatever assertion helper their framework ships, and the marginal value of a shared service is low.

The second is a workflow where baselines are not accepted by a human. The whole model depends on a baseline being validated and accepted, and on a build being a list of runs that a human reviews. In a pipeline where every commit is compared against a fixed reference and any difference fails the build automatically, the service's history and review features are unused, and you have added a network hop and a database to a job that a library does locally.

The third is a repository that cannot hold image baselines. Git is a poor store for large binary diffs that change on every commit, and this project does not describe an external storage option for baselines specifically. The S3 configuration is for the service's own uploads.

The fourth is comparing PDFs and images from a folder with no service. The readme mentions a standalone jar to compare pdf and images from a folder, which is a different tool for a different job, and it tells you that the comparison happens without a server at all.

The fifth is scale. The data model multiplies baselines by branch, OS, browser, viewport and device, and each run is stored. A large matrix across many branches is a storage and retention problem that the readme does not address with pruning or archival guidance.

## Maintenance state, Apache-2.0 and the release cadence

The repository is not archived, and the last push was on 2026-09-01, which is the same day as release 5.8.0. The two releases before it, 5.7.0 and 5.6.0, came on 2026-08-23 and 2026-08-07, so the recent cadence is on the order of one or two releases a month.

The licence is Apache-2.0, with the LICENSE file at the repository root. For a self hosted internal tool that is about as permissive as it gets, and it carries the usual conditions: preserve the notices, state changes, and include the licence text. If you fork it and publish your own images, that is a modification you would need to account for.

The repository carries a .all-contributorsrc and the readme links a Telegram group and a one minute survey about prioritising tasks, which is how a community project solicits direction. The readme also has a Stand With Ukraine banner at the top, which is a statement of position rather than a technical fact.

On the running side, the readme notes that the primary language of the repository is recorded as Shell, which fits the installation script, and that the project can also compare PDFs with a standalone jar. The health of the deployment depends on the published Docker images rather than on anything in this repository, so the version variables VRT_UI_VERSION and VRT_API_VERSION in the compose file are the actual pins you control, and the .env file is where you set them.

One documentation caveat. The readme's integration section is cut off mid-sentence at the point this article was written, so the concrete steps of a first API call, including the endpoint paths and the request body, are not in the repository text. The REST API is the integration point, and you should read the backend documentation or the repository's own docs for the exact contract before writing your first client.

## Conclusion

Adopt Visual Regression Tracker when your visual tests run in more than one framework or CI system and you need a shared place to store baselines, compare results and keep the history of how a baseline drifted. Do not adopt it if a handful of assertion calls inside your existing test suite is all you need, since this is a service with a database, an API and a web UI rather than a library. Before you commit, decide which comparison provider suits each project, since it is configured per project, and confirm that the storage you choose, either the local volume or S3 via the AWS variables, matches where your screenshots are allowed to sit.

## FAQ

### What is Visual Regression Tracker and how does it compare images?

It is a self hosted service that receives images and performs a pixel by pixel comparison against the previously accepted baseline, returning immediate results so unexpected changes are caught. It does not capture screenshots itself, so it works with any automation framework and any platform that can produce a screenshot.

### Which image comparison algorithms does Visual Regression Tracker support?

Four providers: Pixelmatch as the default, which is fast and best for exact pixel differences; Looks-Same for perceptual comparison; Odiff for layout detection and anti-aliasing; and a VLM provider that combines pixelmatch with AI powered human-noticeability analysis. The provider is configured per project in the project settings.

### How do I install Visual Regression Tracker?

With Docker installed, download and run vrt-install.sh, which takes a frontend url, a backend url and a jwt secret argument, or copy docker-compose.yml and .env from the repository, set REACT_APP_API_URL to the right address and start the service with docker-compose up.

### What is a TestVariation in Visual Regression Tracker?

It is the historical record of baselines by name plus branch plus OS plus browser plus viewport plus device. A Baseline is a validated and accepted image, a TestRun is the result of comparing an image against a baseline, a Build is a list of TestRuns and a Project is a list of Builds and TestVariations.

### Can Visual Regression Tracker authenticate against LDAP?

Yes. The api service takes LDAP_ENABLED, LDAP_URL, LDAP_BIND_USER, LDAP_BIND_PASSWORD, LDAP_SEARCH_DN, a users search filter and attribute mappings for last name, first name and mail, along with an LDAP_TLS_NO_VERIFY switch.

## Sources

- [License: Apache-2.0](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker/blob/master/LICENSE)
- [Project website](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker)
- [README](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker/blob/master/README.md)
- [Releases](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker/releases)
- [Visual-Regression-Tracker/Visual-Regression-Tracker on GitHub](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker)

---

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