Your Images. Your Infrastructure. Your Rules.
A self-hosted, OCI-compliant container registry built for internal enterprise use. Single Go binary with an embedded Svelte admin UI, S3-compatible blob storage, PostgreSQL metadata, OIDC SSO, and role-based access — no SaaS, no per-seat fees, no vendor lock-in.
A container registry should be a utility, not a platform. ZenoRegistry does exactly one thing — store and serve OCI images — and runs as a single binary with a Postgres connection string and an S3 bucket.
The OCI Distribution API done right, with the operational controls that internal platform teams actually need.
Implements the complete OCI Distribution Specification: push, pull, tags, catalog, chunked uploads, cross-repo blob mounts, and manifest deletion. Works with Docker, Podman, nerdctl, and any compliant client.
Pluggable storage driver backed by the AWS SDK Go v2. Works with AWS S3, MinIO, Ceph, DigitalOcean Spaces, Scaleway Object Storage, or any S3-compatible service. Content-addressable blob layout with directory fan-out.
Drop-in SSO for the admin dashboard via any OpenID Connect provider — Keycloak, Entra ID, Google Workspace, Authentik, Okta. Emergency local admin always available as break-glass.
Three clean roles: pull, push, and admin. JWT scopes are checked per repository and per action on every OCI request — not bolted on at the proxy layer.
Per-repository policies: keep the last N tags, keep tags pushed in the last N days, never delete tags matching glob patterns like v* or latest, and a configurable safety floor. Always dry-run first.
Supply-chain sibling tags (.sig, .att, .sbom) and multi-arch digest references are recognised, hidden from user-facing tag counts, and excluded from retention rules so signatures never get garbage-collected by accident.
The Go server, OCI API, admin REST API, and embedded Svelte 5 SPA all ship in a single binary. The only runtime dependencies are PostgreSQL for metadata and an S3-compatible bucket for blobs.
Dispatches /v2/* to the OCI Distribution handlers, /api/v1/admin/* to the admin REST API, and falls through to the embedded SPA for everything else.
Bearer JWT for OCI clients, HTTP-only session cookies for the admin UI, OIDC code-exchange callbacks, bcrypt password hashing — all from a single TokenService.
Interface-based design with an S3 driver for production and an in-memory driver for tests. Streaming uploads, chunked PATCHes, atomic completion, and content-addressable layout.
Users, roles, repositories, tag metadata, retention policies, and audit events — pgx connection pool with embedded auto-migrations on startup.
Built with Vite, copied into internal/frontend/dist/, served via //go:embed. No separate process, no nginx in front, no static file mount.
Every standard /v2/ endpoint is implemented. Push from any Docker daemon, pull from any cluster, and use any OCI-compliant tooling you already trust.
HEAD/GET /v2/{name}/blobs/{digest}, PUT /v2/{name}/manifests/{ref}, chunked PATCH uploads, cross-repository blob mount — the full happy path for docker push and docker pull.
GET /v2/_catalog for repositories, GET /v2/{name}/tags/list for tags, with cursor-based pagination via n and last query parameters.
DELETE /v2/{name}/manifests/{ref} for tags and digests. Garbage collection reclaims unreferenced blobs once nothing points at them.
Manifest lists / OCI image indexes are stored, served, and tracked correctly. Bare sha256-<hex> child references are hidden from the UI and excluded from retention.
Docker clients authenticate via the standard OCI Bearer token flow. Browsers log in via OIDC SSO or local admin. Both share the same PostgreSQL user and role tables.
WWW-Authenticate challenge
/v2/token
access claims
CI/CD pipelines push thousands of images per week. ZenoRegistry includes a built-in retention engine so storage cost stays bounded — with safe defaults, dry-run previews, and an audit trail for every automated deletion.
keep_last — N most recent tags
keep_days — tags pushed in last N days
keep_patterns — glob protection (v*, latest, release-*)
keep_min — safety floor per repository
log/slog
A modern Svelte 5 SPA — compiled to static assets and embedded into the Go binary — covers everything operators need without a second service to deploy.
At-a-glance counts of users, repositories, storage usage, and recent activity.
Create, edit, and disable users. Assign and revoke roles. Reset passwords for local accounts.
Search and filter repositories. Drill into tags. Delete tags or whole repositories with confirmation dialogs.
Configure policies per repository, preview the deletion candidates, and execute the run from a single screen.
Designed by people who have run registries in anger. The boring operational details are handled, not punted to the operator.
GET /healthz for Kubernetes liveness and readiness probes. Always-on, dependency-free.
Clean connection draining on SIGINT and SIGTERM — no half-finished blob uploads when you roll a deployment.
Native log/slog output with request IDs, user context, and consistent field names. Drop straight into Loki, Elasticsearch, or ZenoLog.
PostgreSQL schema migrations run automatically on startup. No separate migrator container, no manual SQL to apply.
Point the server at a certificate and key and it serves HTTPS directly — no sidecar proxy required for the simple case.
Plain Deployment + Service + ConfigMap + Secret. A small image, env-var configuration, and a health endpoint — no operator to install.
Every dependency is a load-bearing choice — battle-tested libraries with active maintenance and predictable behaviour.
| Layer | Technology |
|---|---|
| Language | Go 1.25 |
| HTTP Router | chi/v5 |
| Database | PostgreSQL 16 via pgx/v5 |
| Object Storage | S3 (AWS SDK Go v2) — AWS, MinIO, Ceph, Spaces |
| Auth | golang-jwt/v5, bcrypt, coreos/go-oidc/v3 |
| Frontend | Svelte 5 (runes), Vite 8 |
| Logging | log/slog (structured JSON) |
| Container Image | Multi-stage build on Alpine 3.21, ~30 MB |
ZenoRegistry is the right answer when you want full control over your container images without the operational weight of a SaaS or an enterprise registry platform.
Give every team a private registry namespace. Push from CI/CD, pull from clusters, manage access through the IdP you already have.
European data sovereignty, regulated industries, disconnected environments. Single binary plus Postgres plus object storage — that's the whole stack.
Argo CD and Flux pull from the registry. ZenoRegistry plays nicely as the single image source for an entire Kubernetes estate.
Store image signatures, SBOMs, and attestations as cosign sibling tags. ZenoRegistry recognises them, hides them from UI clutter, and never garbage-collects them by accident.
Ships as one of the core services in the ZenoKube sovereign cloud platform — your cluster's own registry, on your hardware, behind your IdP.
Drop-in alternative to GitHub Container Registry, GitLab Container Registry, Harbor, or Quay when you want fewer moving parts and no per-seat invoice.
Run your own OCI registry on your own infrastructure, with SSO from your own IdP and storage in your own bucket. Contact us for a demo, a managed deployment, or a self-hosted licence.