Skip to content

Harbor Reference: Self-Hosted Container Registry — RBAC, Scanning, Replication & Webhooks

Harbor is a CNCF-graduated container registry that adds access control, vulnerability scanning, replication, content trust (Cosign/Notary), and quotas on top of the OCI registry spec. It’s the default self-hosted alternative to Docker Hub and ECR in air-gapped or compliance-heavy environments.

1. Install & Core Architecture

Deploy Harbor on Kubernetes with Helm
# Harbor components:
# - Core: business logic, API
# - Portal: web UI
# - Registry: OCI-compatible image storage (wraps Docker Distribution)
# - Database: PostgreSQL (metadata, users, access rules)
# - Redis: caching, job queue
# - JobService: async tasks (replication, scanning, GC)
# - Trivy: vulnerability scanning adapter
# - Nginx/Traefik: TLS proxy

# Install with Helm:
helm repo add harbor https://helm.goharbor.io
helm install harbor harbor/harbor   --namespace harbor --create-namespace   --set expose.type=ingress   --set expose.tls.enabled=true   --set expose.ingress.hosts.core=registry.example.com   --set externalURL=https://registry.example.com   --set harborAdminPassword=change-me   --set persistence.persistentVolumeClaim.registry.size=100Gi

# Default login: admin / change-me
# First thing: change admin password + create org accounts

# Docker login:
docker login registry.example.com
# User: your-harbor-user | Pass: your-harbor-password

2. Projects, RBAC & Robot Accounts

Multi-tenancy with project isolation, roles, and CI/CD robot accounts
# Projects in Harbor = namespace for images (like ECR namespace or GCR project)
# Three types:
# - Public: anyone can pull (no auth)
# - Private: auth required for both push and pull
# - Proxy cache: proxies Docker Hub / GCR / ECR (with caching)

# Create project via API:
curl -u "admin:change-me" -X POST https://registry.example.com/api/v2.0/projects   -H "Content-Type: application/json"   -d '{"project_name":"backend","public":false,"storage_limit":-1}'

# Roles (assign per project):
# - ProjectAdmin: full control
# - Maintainer: push, delete, manage webhooks
# - Developer: push (tag images to releases)
# - Guest: pull only
# - LimitedGuest: pull specific repos only

# Robot accounts (for CI/CD — not human users):
# Via UI: Project → Robot Accounts → New Robot Account
# Or API:
curl -u "admin:change-me" -X POST   https://registry.example.com/api/v2.0/projects/backend/robots   -H "Content-Type: application/json"   -d '{
    "name": "github-actions",
    "duration": 90,
    "permissions": [{
      "kind": "project",
      "namespace": "backend",
      "access": [
        {"resource": "repository", "action": "pull"},
        {"resource": "repository", "action": "push"}
      ]
    }]
  }'
# Returns: {"name":"robot$backend+github-actions","secret":"abc123..."}
# Use as: docker login registry.example.com -u "robot$backend+github-actions" -p "abc123..."

3. Vulnerability Scanning with Trivy

Automated scan on push, manual scan, and scan policies
# Harbor ships with Trivy as the default scanner (as of Harbor 2.0+)
# Scans run automatically on push if configured in project settings

# Enable scan-on-push per project:
# Project → Configuration → Scan images on push ✓

# Prevent pull if CVEs found (deployment security gate):
# Project → Configuration → Prevent vulnerable images from running
# Severity threshold: Critical, High, Medium, Low, None

# Trigger manual scan via API:
curl -u "admin:change-me" -X POST   https://registry.example.com/api/v2.0/repositories/backend/myapp/artifacts/sha256:abc.../scan

# Get scan results:
curl -u "admin:change-me"   "https://registry.example.com/api/v2.0/projects/backend/repositories/myapp/artifacts?with_scan_overview=true"

# View scan results in UI:
# Project → Repositories → myapp → image digest → Vulnerabilities tab

# CVE allowlist (exception for accepted risk):
# Project → Configuration → CVE Allowlist → Add CVE IDs
# Format: CVE-2023-12345
# Use for: false positives, vulnerabilities in tests-only deps, issues with no fix

# Tag immutability (prevent overwriting release tags):
# Project → Tag Immutability → Add Rule:
# Scope: matching tag = v*.*.*, action: immutable

4. Replication

Push or pull images from/to Docker Hub, ECR, GCR, another Harbor
# Replication = sync images between registries
# Use cases:
# - Mirror Docker Hub to avoid rate limits (pull-based replication)
# - Replicate prod images to DR region
# - Distribute images to edge registries in air-gapped sites

# Add a replication endpoint (e.g. mirror Docker Hub):
curl -u "admin:change-me" -X POST https://registry.example.com/api/v2.0/registries   -H "Content-Type: application/json"   -d '{
    "name": "docker-hub",
    "type": "docker-hub",
    "url": "https://hub.docker.com",
    "description": "Docker Hub mirror"
  }'

# Create replication rule (pull from Docker Hub into harbor):
# Administration → Replications → New Replication Rule
# - Provider: docker-hub
# - Source resource filter: name=library/nginx, tag=1.**
# - Destination: backend/nginx
# - Trigger: event-based (pull triggers sync) or scheduled

# Push-based replication to DR:
# Source: current Harbor | Destination: DR Harbor endpoint
# Trigger: event-based (on push → replicate immediately)
# Override: true (update existing images)

# Proxy cache project (faster — transparent for docker pull):
# Administration → Registries → New → Endpoint type: Docker Hub
# Create Project → Proxy Cache ✓ → Registry: docker-hub
# docker pull registry.example.com/docker-hub-proxy/library/nginx:1.25
# Harbor fetches from Docker Hub on first pull, caches locally

5. Webhooks, Quotas & Garbage Collection

Event webhooks, storage quotas, and reclaim disk space
# Webhooks (notify CI/CD or Slack on push, scan completion, etc.):
# Project → Webhooks → New Webhook
# Events: push artifact, pull artifact, delete artifact, scan completed
# Endpoint: https://hooks.slack.com/... or your CI webhook URL
# Format: JSON or CloudEvents

# Example payload (push artifact):
# {"type":"PUSH_ARTIFACT","occur_at":1710000000,"operator":"robot$backend+github-actions",
#  "event_data":{"resources":[{"tag":"v1.2.3","resource_url":"registry.example.com/backend/myapp:v1.2.3"}]}}

# Storage quotas (per project):
# Administration → Projects → [project] → Quotas
# - Default quota: unlimited (configure before teams start pushing)
# - Recommend: set per-project quota to prevent disk exhaustion
# Example: 50 GB per project, 1000 artifact count limit

# Garbage Collection (reclaim disk from deleted images):
# Administration → Garbage Collection → GC Now
# Schedule: daily 2am to auto-reclaim
# Note: GC doesn't run automatically — must schedule or trigger manually
# Before GC, delete unneeded tags/artifacts (GC only removes unreferenced blobs)

# Tag retention policies (auto-delete old tags):
# Project → Tag Retention → Add Rule
# Example: keep last 10 tags matching "v*.*.*"
#          delete tags older than 30 days matching "dev-*"
# Schedule: weekly or on-push

# Helm chart storage (Harbor supports OCI Helm charts natively):
helm push my-chart-1.0.0.tgz oci://registry.example.com/backend
helm pull oci://registry.example.com/backend/my-chart --version 1.0.0

Track Harbor, Trivy, and container registry tool releases.
ReleaseRun monitors Kubernetes, Docker, and 13+ DevOps technologies.

Related: Trivy Reference | Cosign/Sigstore Reference | Kubernetes RBAC Reference | Docker EOL Tracker

🔍 Free tool: K8s YAML Security Linter — if you deploy Harbor on Kubernetes, check its manifests for 12 security misconfigurations.

Founded

2023 in London, UK

Contact

hello@releaserun.com