Self-hosted service
quay/clair avatar
quay/clair

Clair splits into indexer and matcher, and its main branch can be broken

Vulnerability Static Analysis for Containers

11,093 stars1,215 forksGoApache-2.0

At a glance

What is it?
A Go service that indexes container images and matches them against known vulnerabilities, split into two long-running processes by CLAIR_MODE. Its analysis is static and image based, so nothing it reports describes what a running container is doing.
Who is it for?
Adopt Clair when you want an API that indexes OCI and docker images and lets you write your own policy on the result, and install from a release tag rather than the main branch, which the project warns can be unstable or even broken. Do not expect it to describe a running workload, because the analysis stops at the image.
Can I use it commercially?
Yes. Apache-2.0 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 1 day 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 November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • quay/clair11,093 ★ · +29
Star history
Repository2026-09-222026-11-14
quay/clair11.1k11.1k

29 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

CLAIR_MODE splits Clair into an indexer and a matcher

Clair is not one binary doing one job. The local development compose file runs the same image twice under two service names, and a single environment variable picks the role. This is the whole split in ten lines:

yaml
  indexer:
    <<: *clair-service
    container_name: clair-indexer
    environment:
      CLAIR_MODE: "indexer"
  matcher:
    <<: *clair-service
    container_name: clair-matcher
    environment:
      CLAIR_MODE: "matcher"

The indexer walks an image and records what packages it finds. The matcher compares that record against vulnerability data. Both need the same code, so the split is configuration rather than a different build. Clients drive this through the Clair API: index an image, then match it against known vulnerabilities.

Scale it the way the compose file implies. Two processes means two services in the compose file, and a Postgres that both of them depend on before they will start.

The local compose stack authenticates to Postgres with trust

The development database service is deliberately permissive:

yaml
    environment:
      POSTGRES_HOST_AUTH_METHOD: trust

That removes the password check for any connection reaching the container, which is fine on a laptop and wrong the moment the port is reachable from anywhere else. The consequence of copying this compose file toward production is a database that accepts any user.

The rest of the file is built for observing a run, not for surviving one. YAML anchors at the top pin images for Postgres 15, RabbitMQ 3 and Redis 6, and they also name Grafana, Jaeger, Pyroscope, Prometheus, pgAdmin, skopeo, Traefik and Quay. Several are pinned to a floating tag such as latest, so a rebuild of the same commit can pick up a different sidecar image. Clair's own binary is built with go run in this file, not from a packaged release, so you are testing your working tree.

The main branch is explicitly not a stable binary source

The first note in the README is a warning: the main branch may be in an unstable or even broken state during development, and you should use releases instead of main in order to get stable binaries. That is unusual candour in a project README and it changes how you install.

The release history shows why the warning is not paranoia. Tags land at v4.7.4 on 2024-05-01, v4.8.0 on 2024-10-09 and v4.9.0 on 2025-12-10, gaps of five months, then fourteen. The last push to this repository was on 2026-10-30, which is code that has moved well past v4.9.0 and is not covered by any tag.

So there are two states to choose between, and they are not equivalent. A release tag gives you a fixed commit and a version string. main gives you recent fixes and a moving target. Record which one you deployed.

Configuration is a file path, not a set of environment variables

The compose file passes configuration with a command line argument and a default path:

yaml
    command:
      - go
      - run
      - .
      - -conf
      - /etc/clair/${CLAIR_CONFIG:-config.yaml}

The config directory is mounted read only from local-dev/clair, and CLAIR_CONFIG only chooses which filename inside it is read. Above that block sits a comment explaining why the image is not configured through the environment instead: maps are not recursively merged, so an env var cannot override a nested key without replacing the whole map.

That constraint decides your deployment shape. A twelve-factor setup where every setting arrives as an environment variable will not map cleanly onto this configuration, and you will end up templating a file. config.yaml.sample at the repository root is the starting point for that file, and the compose file's use of a read-only mount means your process cannot write back to it.

The README is a signpost, and the book is the documentation

The README for this project is about a hundred and fifty words and says where the documentation lives rather than repeating it: the book at quay.github.io/clair contains all the documentation on Clair's architecture and operation. A book.toml file at the repository root is what builds it, and a Documentation/ directory sits alongside.

What that costs you is immediate. The README names no configuration key, no API endpoint, no matcher tuning option and no upgrade procedure. It points at the OCI image specification and the docker image specification v1.2 as the format sources it currently includes, and it explains the name, which comes from the French term for clear, bright or transparent, after the goal of a more transparent view of container security.

For an evaluation that means your reading time goes into the book rather than into a quick skim of the front page. For an upgrade, check ROADMAP.md and CHANGELOG.md in the repository, since neither the README nor the release titles describe what changed between v4.8.0 and v4.9.0.

go.mod pulls three claircore modules and needs Postgres

The module is github.com/quay/clair/v4 and it declares go 1.26.0. Three dependencies come from Clair's own ecosystem: github.com/quay/claircore at v1.6.2, github.com/quay/claircore/toolkit at v1.7.0 and github.com/quay/clair/config at v1.4.3. Two Claircore packages at different minor versions in one build is worth watching, because the core library holds the vulnerability data and layer parsing that the indexer and matcher both rely on.

State lives in Postgres, reached through github.com/jackc/pgx/v5 at v5.11.0, with github.com/remind101/migrate in the same list for schema changes. github.com/google/go-containerregistry at v0.22.1 handles image access, and github.com/rabbitmq/amqp091-go at v1.13.0 is present for messaging.

Observability is unusually heavy for a scanner of this size: the OpenTelemetry SDK at v1.46.0 with Jaeger, OTLP gRPC, OTLP HTTP, stdout and Prometheus exporters, plus prometheus/client_golang and grafana/pyroscope-go/godeltaprof for profiling. Every one of those exporters is a live endpoint you have to decide about before exposing the process.

Static analysis stops at the image, not at the running container

Clair's scope is static analysis of vulnerabilities in application containers, currently including OCI and docker image formats. That word static is the boundary. Everything the tool reports describes layers that were built and pushed, and nothing about the process that came out of them.

Three consequences follow for a reader. A runtime that installs packages after the image was built is invisible to it, because the analysis never touches the running filesystem. A tag that pointed at a different digest than the one indexed produces a verdict about content you are not running. And results are only as current as the vulnerability data they were matched against.

The README does not document an image re-index interval, a matcher update schedule, or what a client is expected to do when it has no fresh data. Those are policy decisions your side has to make.

Editorial conclusion

Adopt Clair when you want an API that indexes OCI and docker images and lets you write your own policy on the result, and install from a release tag rather than the main branch, which the project warns can be unstable or even broken. Do not expect it to describe a running workload, because the analysis stops at the image. Before wiring anything up, read config.yaml.sample and confirm both processes can reach the Postgres instance you have, since the indexer and the matcher each persist state there.

Frequently asked questions

What does Clair do?

Clair performs static analysis of vulnerabilities in application containers, currently including OCI and docker image formats. Clients use the Clair API to index their container images and then match that index against known vulnerabilities.

How do I install Clair?

The README gives no install command and instead directs you to the releases page for stable binaries, warning that the main branch may be in an unstable or even broken state. Architecture and operating detail lives in the documentation at quay.github.io/clair.

What license is Clair under?

Clair is under the Apache 2.0 license, with the terms in the LICENSE file at the repository root. Both the Dockerfile and the Makefile carry Apache License 2.0 headers, and a NOTICE file sits beside the license.

What Go version does Clair need?

The go.mod file declares go 1.26.0 for the module github.com/quay/clair/v4. The Dockerfile builds against quay.io/projectquay/golang with GO_VERSION set to 1.25, and the local development compose file pins the same golang 1.25 image.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. quay/clair on GitHub
  4. README
  5. Releases
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/quay-clair.svg)](https://hysenlabs.com/projects/quay-clair)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/quay-clair.svg)](https://hysenlabs.com/projects/quay-clair)

Was this useful?