Self-hosted service
project-zot/zot avatar
project-zot/zot

project-zot/zot: a vendor-neutral OCI registry written in Go

zot - A scale-out production-ready vendor-neutral OCI-native container image/artifact registry (purely based on OCI Distribution Specification)

2,853 stars281 forksGoApache-2.0

At a glance

What is it?
A container image registry that claims nothing beyond the OCI image and distribution specifications, and spends its complexity budget on storage backends, signing policy and conformance instead.
Who is it for?
zot is at its best when you want an OCI registry that nobody has to explain the vendor lock-in of, because the project's whole premise is that it speaks the published specifications and nothing more.
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 October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A registry that promises only the OCI specifications

The README is unusually short, and its opening sentence is the whole pitch: zot is a production-ready vendor-neutral OCI image registry, with images stored in the OCI image format and served over the wire using the distribution specification, and the README adds a blunt that's it.

That framing matters for a registry. Registries are where lock-in hides, because once images land in one, the push and pull path becomes operational infrastructure you cannot easily move. A registry built strictly to two published specifications keeps that door open: any client that speaks the distribution API should work, and any storage layout you can back up yourself is fair game.

The repository topics line up with the claim: oci, oci-distribution, distribution-spec, opencontainers, kubernetes, helm and zot. The build badge story matches too, since the README shows badges for build, test, nightly, codecov, OCI conformance, CodeQL, CII Best Practices, OpenSSF Scorecard, FOSSA and the Go package reference. Conformance is the interesting one, because a project claiming spec compliance can prove it against an external test suite rather than asking you to take its word.

The licence is Apache-2.0, and the tree carries the supporting paperwork a project needs to survive audits: `LICENSE`, `NOTICE`, `THIRD-PARTY-LICENSES.md`, `SECURITY.md`, `CODEOWNERS` and `MAINTAINERS.md`.

The go.mod file reads as a feature list

Rather than paraphrase the feature documentation, it is more revealing to read what the module actually imports. The module path is `zotregistry.dev/zot/v2`, which tells you the project's own domain doubles as its import path:

go
module zotregistry.dev/zot/v2

go 1.27.0

The storage story is visible in the cloud SDKs: Azure blob storage, the AWS SDK v2 with DynamoDB, ECR and Secrets Manager support, plus miniredis and go-redis for Redis. A registry that can keep its metadata in DynamoDB and its blobs in S3 is a registry that fits an existing AWS account rather than demanding new hardware.

The security and policy machinery is equally legible. `coreos/go-oidc` covers OIDC tokens, `go-ldap` covers directory-backed identity, `golang-jwt` covers token verification, `cel-go` covers policy evaluation, and the presence of both `didip/tollbooth` and `gqlgen` says there is a rate limiter and a GraphQL endpoint. Trust verification pulls in the aquasecurity Trivy packages, which is how vulnerability scanning gets into a registry rather than into a separate CI stage.

Two dependencies deserve attention because they signal what the project considers non-negotiable. It imports `distribution/distribution/v3`, the reference implementation of the distribution spec, which is how a from-scratch registry stays protocol-compatible. And it imports `google/go-containerregistry`, which lets zot act as a client for other registries, which is how mirror and sync features are built.

Pinned tool versions in the Makefile show the release discipline

The root Makefile is the best available evidence of how seriously this project takes reproducible tooling, and it is worth reading even if you never build from source. It pins exact versions for everything the CI uses: golangci-lint at v2.13.2, notation at 1.3.2, cosign at 3.0.6, oras at 1.2.1, helm at v3.9.1, regctl at v0.10.0, crictl at v1.26.1, kind at v0.31.0, skopeo at v1.12.0 by default, plus bats for shell tests and stacker for container builds.

Two further details show the project has been bitten by real issues. `GOEXPERIMENT=jsonv2` is exported globally, which means the build depends on a specific Go JSON implementation rather than the default. And `COMMIT` is computed with a `-dirty` suffix when the working tree has uncommitted changes, so a build can never quietly claim to be a clean commit.

The `USE_PREBUILT` variable is a small but thoughtful design. Set to 1, the build reuses binaries already downloaded into `bin/` by CI, which is how the container image build avoids recompiling everything from scratch. Left at 0, phony targets compile the binaries directly.

The container runtime is also detected rather than assumed: the Makefile prefers podman when `command -v podman` finds it and falls back to docker.

v2.1.21 fixed garbage collection and Kubernetes auth reloads

The release notes are unusually specific, which makes them a decent proxy for where the project's rough edges were. v2.1.21, published on 2026-09-06, is dominated by correctness fixes in the storage layer. Garbage collection now collects referenced blobs once per repository during cleanup rather than repeatedly, and a separate change breaks an orphan cycle when manifest blobs are missing from storage. Another fix makes the deduplication walk visit nested-namespace repositories, which is the kind of bug that only appears once you use more than one repository level.

The auth fix is the one that will resonate with anyone running this in Kubernetes: htpasswd hot reload now works for secret mounts, rather than only for files on disk. Kubernetes updates mounted secrets by swapping a symlinked directory, so a watcher that only stats the file misses the change entirely. Fixing it means credential rotation stops requiring a pod restart.

The trust side also moved. Image trust gained recognition of sigstore v3 bundles, and an earlier release added TSA truststore support to the notation policy while decoupling signature trust processing from scanning. v2.1.19 aligned blob, manifest and referrers handling with OCI conformance and added exponential HTTP retry backoff via `maxRetryDelay`, which is what you want when a storage backend hiccups during an image push.

GitHub reports the last push on 2026-09-23, and the repository is not archived.

The examples directory is where configuration decisions get made

A registry's configuration surface is large enough that the examples directory is more useful than the documentation for a first evaluation. The listing includes a single-node BoltDB configuration, DynamoDB and Azure backends, a config that puts everything remote, and a batch of security-shaped examples: bearer auth, combined authentication, anonymous with authorization rules, OIDC with workload identity, and two AWS Secrets Manager bearer auth recipes that come with shell scripts and a Trivy ignore file.

Read them in that order and a picture forms. There is a `config-conformance.json` for running the OCI conformance suite locally, a `config-bench.json` for benchmarking, a `config-cve.json` and `config-cve-trivy.json` pair for vulnerability scanning, a `config-docker-compat.json` and `config-docker-compat-sync.json` pair for Docker compatibility and mirroring, and a `config-events.json` for CloudEvents, which lines up with the NATS CloudEvents dependency in `go.mod`.

The `cluster/` directory under examples is for Kubernetes deployment. The top-level tree also has `demos/`, `swagger/` for the API surface, `docs/`, `test/`, `pkg/`, `cmd/` and `errors/`, plus `zot.go` at the root as the package entry point.

One tension worth flagging: the repository description calls zot a scale-out registry, which implies a clustered mode, while several example configs are clearly single-node. The clustered deployment guidance is on the documentation site rather than in the examples listing, so confirm which mode you need before choosing a config.

The web UI lives in a separate repository

If you go looking for the zot interface in this repository, it is not there. The Makefile pins the UI as an external artifact, with `ZUI_REPO_OWNER` set to project-zot, `ZUI_REPO_NAME` set to zui and `ZUI_VERSION` pinned to a specific commit rather than a tag. Releases note a zui version bump alongside registry changes, which means the interface is versioned in step with the server but developed separately.

That split is a reasonable architecture, since a registry API and a browser interface have very different security exposure. It also means an evaluation has two repositories to look at rather than one.

For positioning, `COMPARISON.md` in the repository root is the file to read. A project whose README is fifteen lines will put its argument somewhere else, and for a registry competing with Harbor and with the distribution reference implementation, an honest comparison table is more informative than a feature list. The related search terms around this project include Zot versus Harbor and Zot docker, so that comparison is where readers are already landing.

The repository is written in Go, is Apache-2.0 licensed, sits at 2.1.21, and publishes its documentation at zotregistry.dev rather than in the README.

Editorial conclusion

zot is at its best when you want an OCI registry that nobody has to explain the vendor lock-in of, because the project's whole premise is that it speaks the published specifications and nothing more. The `go.mod` dependency list is the honest summary of what that costs: object storage for Azure, S3 and DynamoDB, Trivy for scanning, cel-go for policy, OIDC and LDAP for identity, cosign and notation for trust, and the distribution reference library for protocol parity. v2.1.21 came out on 2026-09-06 with fixes for garbage collection and htpasswd hot reload on Kubernetes secret mounts, and GitHub reports the last push on 2026-09-23. The README itself is fifteen lines and points at zotregistry.dev, so the documentation site, not the repository page, is where evaluation happens. Start with `examples/config-boltdb.json` for a single-node configuration, read `COMPARISON.md` for how the project positions itself against Harbor and the distribution registry, and remember that the UI is a separate repository, project-zot/zui.

Frequently asked questions

What is the zot project?

zot is a container image registry written in Go and described in its README as a production-ready vendor-neutral OCI registry. Images are stored in the OCI image format and served using the OCI distribution specification, with documentation at zotregistry.dev and an Apache-2.0 licence.

How does zot compare with Harbor?

Harbor is a fuller platform built around a registry, with scanning policies, replication and project-level roles, while zot is deliberately closer to the distribution specification with optional scanning and policy through Trivy and cel-go. The repository root carries a `COMPARISON.md` written for exactly this question, and zot publishes a conformance badge as evidence of specification parity.

Can zot store images in S3 or other cloud backends?

The module dependencies include Azure blob storage and AWS SDK v2 packages for DynamoDB, ECR and Secrets Manager, alongside Redis and BoltDB. The `examples/` directory holds ready configurations including `config-dynamodb.json`, `config-azure.json` and `config-all-remote.json`, which is the fastest way to see how a remote storage layout is wired.

Does zot scan images for vulnerabilities?

The go.mod requires the aquasecurity Trivy packages, and the examples directory includes both `config-cve.json` and `config-cve-trivy.json`. A release also decoupled signature trust processing from scanning, so signing policy and vulnerability scanning are configured separately.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. project-zot/zot 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/project-zot-zot.svg)](https://hysenlabs.com/projects/project-zot-zot)