Harbor is a private OCI registry that adds projects with role-based access control, vulnerability scanning, image signing, replication between registries and tag retention rules on top of upstream Docker Distribution. It replaces a paid Docker Hub team plan, Artifactory or a cloud provider's managed registry.
Our verdict on Harbor#
Be clear-eyed about the cost. A plain registry:2 is one container, about 30 MB of memory, and a config file. Harbor is roughly ten containers, a bundled PostgreSQL and Redis, a documented 4 GB minimum with 8 GB recommended, and 40 GB of disk before you push anything. You are paying that to get things registry:2 genuinely does not have: projects with per-role access control, OIDC login, Trivy scanning with policies that can block pulls of vulnerable images, pull-through proxy caches, replication to and from other registries, tag retention rules and quotas, and a web UI that non-operators can use. If your requirement is a private place to push images inside your own network, run registry:2 and stop reading. If you need to prove who pushed what, keep a CVE posture, or mirror upstream images so builds survive a Docker Hub rate limit, Harbor earns its footprint. Official arm64 images only landed in mainline during 2026 and released tags are still amd64.
Who Harbor is for#
Choose it if
- Teams that must show an audit trail of who pushed which image and what CVEs it carried at the time.
- Anyone hitting Docker Hub pull rate limits, since Harbor's proxy cache projects mirror upstream transparently.
- Multi-site setups replicating a golden image set from one registry to another on a schedule.
- Kubernetes clusters that need image pull credentials scoped per namespace rather than one shared account.
Look elsewhere if
- You just need somewhere private to push images;
registry:2does that in one container and 30 MB. - Your host is arm64, because released Harbor image tags are still amd64 only and the multi-arch work is in dev builds.
- You have under 4 GB of RAM free, which is the documented minimum before Trivy and PostgreSQL start competing.
What running it actually looks like#
You download an installer tarball, edit harbor.yml and run install.sh, which generates a docker-compose file and starts the stack. The offline installer bundles all images and is large; the online one pulls them at install time. harbor.yml sets hostname, the TLS certificate and key paths, harbor_admin_password (default Harbor12345, used only on first boot) and the database password. Harbor ships no certificates, and the docker client refuses a plain-HTTP registry unless you add it as insecure on every node, so get TLS right before anything else. The running stack is a proxy, core, portal, job service, registry, registryctl, PostgreSQL, Redis, Trivy and an exporter. data_volume defaults to /data. Changing hostname later means re-running the prepare step and restarting the whole stack.
Resource profile#
- Memory
- 2500 MB idle, around 4500 MB in ordinary use. Documented minimum is 4 GB with 8 GB recommended; the figures here are observed across the full container set, where PostgreSQL, Redis, the core service and the Trivy scanner each take a slice before a single image is pushed.
- CPU and acceleration
- Documented minimum is 2 CPUs and 4 is recommended. Idle load is low; Trivy scans and garbage collection are the spikes, and a scan of a large image will saturate a core for a minute.
- Storage growth
- Documented 40 GB minimum and 160 GB recommended. Layer blobs dominate and deleted tags do not free space until you run garbage collection, so real usage runs well above the sum of your current images.
- Operational load
- 4 of 5, Heavy. Ten coupled containers sharing a PostgreSQL that migrates its own schema on startup, upgrades supported only from v2.11.0 and later, a garbage collector that wants read-only mode, and a restore that requires the data volume and the database to be consistent with each other.
Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.
Data and backup surface#
Two things, and they must be captured together. /data/database is the bundled PostgreSQL holding projects, users, robot accounts, policies, quotas and the artifact metadata. /data/registry is the blob store holding the actual layers. A snapshot of one without the other leaves you with images the UI cannot see, or metadata pointing at blobs that are gone. Stop the stack, copy the whole /data tree plus your harbor.yml and the common/config directory generated by the installer, then start again; the upgrade documentation is emphatic that you back up before any migration. Trivy's vulnerability database under /data/trivy-adapter is regenerable and can be excluded, at the cost of a re-download on first scan.
Derived backup shape
Postgres dump. pg_dump (or pg_dumpall) into a file, then snapshot that file plus the app's data volume.
Traps and surprises#
Deleting tags does not free disk
Removing an artifact in the UI only unlinks metadata. The layer blobs stay until you run garbage collection from the admin panel, and Harbor puts the registry into read-only mode while it runs, so pushes fail for the duration. On a registry with years of CI tags the first GC can take hours and free hundreds of gigabytes. Schedule it, and set tag retention rules so it never gets that far again.
Upgrades are only supported from v2.11.0 and later
The upgrade guide covers migration from v2.11.0 to current and sends older installs to archived guides for a chain of intermediate hops. Schema migration happens inside the core container on startup, so a failed migration leaves a half-upgraded database with no automatic rollback. Take a full copy of /data/database first, and step through majors rather than jumping.
A bundled PostgreSQL major upgrade runs on container start
Harbor has moved its internal PostgreSQL major version between releases, and the upgrade runs pg_upgrade automatically when the database container starts on the new version. That is a one-way conversion happening during what looks like a routine restart. Read the release notes for the version you are moving to and snapshot /data/database beforehand, because rolling the image tag back will not roll the data directory back.
Released images are amd64 only
Full multi-architecture enablement merged into Harbor's mainline during 2026, and the dev image tags now publish linux/arm64 alongside amd64, but the tagged release images are still amd64. On a Graviton instance or an arm64 homelab node you are choosing between emulation, a community-built image set, or waiting for the first release that carries the multi-arch manifests. Check the manifest of the exact tag before you plan around it.
Switching authentication mode is one-way
Harbor lets you move from its local database auth to LDAP or OIDC, but once users exist under the new mode you cannot cleanly go back, and existing local accounts do not automatically map onto external identities. People discover this after onboarding a team locally and then wiring up OIDC. Decide the auth backend before you create users, and use robot accounts for CI rather than human accounts either way.
Specifications#
- Category
- Git forges and developer infrastructure
- Licence
- Apache-2.0 (Permissive)
- Written in
- Go / Angular
- First release
- 2016
- Maturity
- Mature
- Datastore
- PostgreSQL plus Redis, both bundled
- Default ports
80,443- Ships as
- docker compose (offline/online installer), helm chart, kubernetes operator
- arm64 builds
- No
- Identity
- Native OIDC. OIDC and LDAP/AD are first-class auth backends configured in the admin UI, with OIDC group claims mapped onto Harbor project roles; switching auth mode after users exist is one-way.
- Replaces
- Docker Hub Team, JFrog Artifactory, Amazon ECR, Azure Container Registry
- Project site
- goharbor.io
- Source
- github.com/goharbor/harbor
- Documentation
- goharbor.io/docs
Alternatives to Harbor#
Everything else in git forges and developer infrastructure, closest in operational weight first.
| Tool | Ops load | Idle RAM | Licence |
|---|---|---|---|
| GitLab CE | 4, Heavy | 4096 MB | MIT |
| Forgejo | 2, Light | 150 MB | GPL-3.0-or-later |
| Gitea | 2, Light | 150 MB | MIT |
| Woodpecker CI | 2, Light | 90 MB | Apache-2.0 |
| code-server | 2, Light | 200 MB | MIT |
Where Harbor comes up elsewhere#
Head to head
Migration guides
Sources#
Facts on this page were checked against the project's own material. Where a number is our own estimate rather than a published figure, the text says so.
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.