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