
September 11, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Your CI pipeline builds images every deploy. Pushing them to a public hub is a bad default. A Harbor: Private Container Registry gives you a self-hosted store for Docker and OCI images with RBAC, vulnerability scanning, replication, and retention policies. I've used private registries on production Linux servers where small teams need control without paying per-seat SaaS fees. This guide covers architecture, install steps, CI wiring, and the operational gotchas that bite after go-live. For broader context, see our container registry comparison across Docker Hub, GitLab, and ECR.
What is Harbor and why do teams choose a private container registry?
Harbor is an open-source registry built on the OCI Distribution Specification. It wraps the core push/pull API with a web UI, LDAP/OIDC auth, robot accounts, webhooks, and policy engines. You run it on your own VPS, on-prem rack, or private cloud.
Public hubs leak metadata. They also rate-limit pulls. For a law-firm portal or booking app on shared EC2, that friction shows up during deploys. Harbor keeps images inside your network boundary. Pulls stay fast. Tags stay private.
Harbor adds features the basic self-hosted Docker Registry lacks out of the box. You get project-level isolation, immutable tags, garbage-collection scheduling, and built-in scanning hooks. That matters when compliance asks who pushed what and when.
Core Harbor components
- Core service — API, auth, project management, replication rules.
- Registry — OCI-compatible blob storage (local disk or S3-compatible backend).
- Job service — Async tasks: scan, replicate, GC, webhooks.
- Portal — Web UI for admins and developers.
- Scanner adapter — Usually Trivy for CVE detection.
Harbor fits teams that outgrow a single-node registry but do not want full cloud lock-in. It also pairs well with containerd as the runtime on Ubuntu hosts you already manage.
How do you install Harbor on Ubuntu for a production-ready private registry?
Harbor ships as a Docker Compose bundle. You prepare TLS, run the installer, then tune harbor.yml. Plan disk early. Image layers add up fast on active CI pipelines.
Prerequisites
- Ubuntu 22.04 or 24.04 VM with 4 vCPU, 8 GB RAM minimum for small teams.
- Docker Engine and Docker Compose plugin installed.
- A DNS A record, e.g.
registry.example.com, pointing at the server. - TLS certificate — Let's Encrypt via Certbot works on port 443.
- Open ports 443 (Harbor UI/registry) and 80 only if you redirect HTTP.
Install steps
Download the official offline or online installer from the Harbor release page. Extract and edit config before the first run.
# Example paths on Ubuntu
cd /opt
wget https://github.com/goharbor/harbor/releases/download/v2.11.0/harbor-online-installer-v2.11.0.tgz
tar xzvf harbor-online-installer-v2.11.0.tgz
cd harbor
cp harbor.yml.tmpl harbor.yml
nano harbor.yml Set hostname, HTTPS cert paths, and admin password in harbor.yml. Enable Trivy in the installer flags if you want scanning from day one.
hostname: registry.example.com
https:
port: 443
certificate: /etc/letsencrypt/live/registry.example.com/fullchain.pem
private_key: /etc/letsencrypt/live/registry.example.com/privkey.pem
harbor_admin_password: "ChangeMeOnFirstLogin"
# data volume — use a dedicated mount
data_volume: /data/harbor Run the installer with the Trivy scanner included.
sudo ./install.sh --with-trivy Log in at https://registry.example.com. Change the default admin password immediately. Create a project per app or per environment. Set the project to private unless you have a deliberate public mirror.
On real client infrastructure I treat Harbor like any other production service. Backups cover /data/harbor and the Postgres volume. Cert renewal goes into cron. UFW allows 443 from deploy runners and cluster nodes only. That mirrors how I harden other Linux production servers.
How does Harbor compare to Docker Registry, GitLab, and cloud ECR?
Not every team needs Harbor. A plain registry suffices for a solo developer. Cloud ECR wins when you live entirely inside AWS. Harbor shines when you want one private hub across mixed environments.
| Criteria | Harbor | Docker Distribution | GitLab Container Registry | AWS ECR |
|---|---|---|---|---|
| Self-hosted control | Full | Full | With GitLab | Managed |
| Built-in UI & RBAC | Yes | No (needs add-ons) | Yes | IAM-based |
| Vulnerability scanning | Trivy built-in | No | With Ultimate tier | ECR scanning |
| Cross-registry replication | Yes | No | Limited | Replication rules |
| Operational overhead | Medium | Low | Low if GitLab exists | Low |
| Cost model | Infra + ops time | Infra only | License / SaaS | Per-GB + pull |
For a side-by-side of public and managed options, our Docker Hub vs GitLab vs ECR guide fills in pricing and pull-limit details Harbor does not address.
Verdict: choose Harbor when you need enterprise-style registry features on your own metal or VPS. Skip it if GitLab already hosts your repos and images. Skip it if you are all-in on one cloud registry and never replicate elsewhere.
How do you push and pull images to Harbor from CI/CD pipelines?
Harbor speaks the standard Docker Registry HTTP API V2. Any tool that supports docker push works. Use robot accounts for CI. Never embed personal user passwords in pipeline variables.
Create a robot account
- In Harbor UI, open your project → Robot Accounts.
- Create a robot with push and pull permissions scoped to that project.
- Copy the generated token once. Harbor will not show it again.
GitLab CI example
variables:
HARBOR_REGISTRY: registry.example.com
HARBOR_PROJECT: myapp
IMAGE_NAME: $HARBOR_REGISTRY/$HARBOR_PROJECT/api
build-and-push:
stage: deploy
image: docker:27-cli
services:
- docker:27-dind
before_script:
- echo "$HARBOR_ROBOT_TOKEN" | docker login $HARBOR_REGISTRY -u "$HARBOR_ROBOT_NAME" --password-stdin
script:
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:latest
- docker push $IMAGE_NAME:latest Store HARBOR_ROBOT_TOKEN and HARBOR_ROBOT_NAME as masked CI variables. Tag images with the Git commit SHA. Mutable latest tags are fine for dev. Use immutable tags or digest pins in production.
On Kubernetes, create an image pull secret from the same robot credentials. Reference it in the pod spec or service account. For Docker hosts, run docker login once on the deploy user. Better yet, use a credential helper.
This pattern matches how I wire automated container builds elsewhere. Only the registry endpoint changes. The build stage stays the same.
Pull by digest for reproducible deploys
# After push, read digest from Harbor UI or API
docker pull registry.example.com/myapp/api@sha256:abc123...
# In Kubernetes deployment manifest
image: registry.example.com/myapp/api@sha256:abc123... Digest pins stop silent overwrites when someone retags latest. Pair them with Harbor's immutable tag project setting for release branches.
How do you enable vulnerability scanning and security policies in Harbor?
Harbor integrates Trivy for layer scanning. Enable scan-on-push at the project level. Block deployments when critical CVEs appear. That closes the gap between "we built an image" and "we ship safe code."
Our dedicated guides on container image scanning with Trivy and Trivy for containers and IaC explain scanner flags in depth. Harbor wraps Trivy so developers see results in the UI without running CLI scans locally.
Scan-on-push configuration
- Project → Configuration →勾选 Automatically scan images on push.
- Set a CVE allowlist for base images you cannot patch yet.
- Configure webhooks to notify Slack or your ticket system on scan failure.
Add a deployment policy: reject pulls or warn when severity ≥ High. Harbor 2.x supports vulnerability severity thresholds per project. Test the threshold against real images first. Alpine and distroless bases report different noise levels.
For supply-chain hardening, combine scanning with Cosign image signing. Harbor supports Notary in older setups. Cosign is the modern path for keyless or keyed signatures in 2026 pipelines.
How do you operate Harbor reliably in production?
Installation is one afternoon. Operations are ongoing. Disk fills up. Certificates expire. Replication jobs stall. Treat Harbor as production infrastructure, not a sidecar experiment.
Storage and garbage collection
Harbor deduplicates layers, but orphaned uploads still happen after failed pushes. Schedule GC during low-traffic windows. The UI under Administration → Garbage Collection triggers a run. Automate it weekly via the Harbor API if your version supports cron-style jobs.
For larger fleets, point blob storage to S3-compatible object storage. Local SSD works up to a few hundred GB. Beyond that, object storage scales cheaper. Monitor free space with your existing host alerts.
Replication and DR
Harbor can replicate projects to a second Harbor instance or to cloud registries. That gives you a warm standby without rebuilding images during an outage. Test failover pulls quarterly. An untested replica is wishful thinking.
If you also run containers on ECS with Fargate, replicate select projects to ECR as a hybrid pattern. Harbor stays canonical. ECR serves the AWS region.
Backups
Back up:
- Harbor data directory (registry blobs and internal configs).
- PostgreSQL database (project metadata, users, policies).
- TLS certificates and
harbor.yml.
Restore drills matter. A registry you cannot rebuild blocks every deploy. Store backups off-server. Encrypt them at rest.
Resource limits and hardening
Run Harbor components with memory caps like any other stack. Our guide on limiting Docker container resources applies to the Compose services Harbor ships.
Enable rootless container practices on surrounding hosts where feasible. Harbor itself expects Docker with sufficient privileges on the install node. Isolate that node on a management VLAN. Restrict admin UI access by IP or VPN.
Rotate robot account tokens on the same schedule as database passwords. Use separate robots per pipeline. One leaked token should not grant push access to every project.
For multi-service apps, Harbor often sits beside app containers defined in Docker Compose multi-container stacks. Keep registry data on fast disk separate from app logs. Noisy neighbours slow pushes when IO is shared.
Common production mistakes
Teams skip TLS on internal registries. Man-in-the-middle risk is real even on private VLANs. Always terminate HTTPS. Trust corporate CAs on build agents.
Another mistake is one shared admin account for humans and CI. Robot accounts exist for a reason. Audit logs become useless when every push shows "admin."
Finally, never run Harbor on the same disk as your only database backup target. A full registry can starve IO and stall backups. Budget Rs 3,000–8,000/month (~USD 22–60) for a dedicated 200 GB SSD VPS in Nepal hosting if you are cost-sensitive but still need private images.
Key Takeaways
- Harbor is the right private registry when you need RBAC, scanning, replication, and a UI—not just blob storage.
- Install with the official Compose bundle, TLS on day one, and Trivy via
./install.sh --with-trivy. - Use robot accounts in CI; pin production deploys to image digests or immutable tags.
- Enable scan-on-push and severity policies before your first production workload lands.
- Schedule garbage collection, monitor disk, back up Postgres and
/data/harbor, and test replication failover quarterly. - Compare Harbor against GitLab Registry and cloud ECR honestly—do not self-host if managed options already fit your stack.
People Also Ask
Is Harbor free to use?
Yes. Harbor is open source under the Apache 2.0 license. You pay for compute, storage, bandwidth, and the time to operate it. There is no per-user license fee like some commercial registries.
Can Harbor replace Docker Hub entirely?
For private images, yes. Public base pulls still often come from Docker Hub or mirror registries. Many teams configure Harbor as a pull-through cache or proxy for upstream hubs to avoid rate limits.
Does Harbor work with Kubernetes?
Harbor is Kubernetes-friendly. Create image pull secrets from Harbor credentials. Install the Harbor Helm chart if you want Harbor itself on Kubernetes instead of the default Compose installer on a VM.
How is Harbor different from GitLab Container Registry?
GitLab's registry is convenient when repos and pipelines already live in GitLab. Harbor is registry-first: stronger cross-registry replication, standalone RBAC, and scanning without tying you to a Git vendor. Some orgs run both—GitLab for dev, Harbor as the canonical production hub.
Ship a Harbor private container registry your team can trust
A well-run Harbor: Private Container Registry turns image storage from an afterthought into part of your security posture. You control access, scan every push, and replicate for resilience without shipping code to a public hub. Start small: one project, one pipeline, scan-on-push enabled. Expand replication and signing once the basics work.
If you want help sizing a VPS, hardening TLS, or wiring Harbor into GitLab CI on Ubuntu, I offer hands-on enterprise application and DevOps support and ongoing production maintenance. See how similar infrastructure work shows up in projects like Adventure Third Pole Trek. Validate webhook payloads with our JSON formatter before they hit your alert channels. Read more on admission controllers and webhooks if you enforce policies at the cluster edge. Visit kokil.com.np or contact us to plan your registry rollout.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

