Harbor: Enterprise Container Registry Complete Guide
Harbor is an open-source, cloud-native container registry that stores, signs, and scans container images and Helm charts. Graduated from the CNCF, Harbor adds enterprise features like vulnerability scanning, content trust, replication, RBAC, and audit logging on top of the Docker registry. This guide covers Harbor deployment, configuration, and production operations.
What is Harbor?
Harbor extends the Docker registry with enterprise capabilities:
Key Features
- Vulnerability Scanning: Integrated Trivy scanning for all images
- Content Trust: Image signing with Cosign and Notation
- RBAC: Project-level role-based access control with LDAP/OIDC
- Replication: Image replication across registries (push/pull)
- Proxy Cache: Cache remote registries (Docker Hub, GCR, ECR)
- Helm Charts: OCI-native Helm chart repository
- Quota Management: Per-project storage quotas
- Audit Logging: Full audit trail for compliance
Harbor vs. Other Container Registries
| Feature | Harbor | Docker Hub | Quay | GitLab CR | Nexus | ACR/ECR/GCR |
|---|---|---|---|---|---|---|
| Cost | Free/OSS | Freemium | Freemium | Included | Freemium | Pay-per-use |
| Self-Hosted | Yes | No | Yes | Yes | Yes | No |
| Vulnerability Scan | Trivy | Docker Scout | Clair | Trivy | Limited | Built-in |
| Content Trust | Cosign/Notation | Notary | No | No | No | Sigstore |
| Replication | Yes | No | Yes | No | Yes | No |
| Proxy Cache | Yes | No | No | Yes | Yes | No |
| RBAC | Projects | Orgs | Teams | Groups | Roles | IAM |
| Helm Charts | OCI native | Yes | No | Yes | Yes | Yes |
| OCI Artifacts | Yes | Yes | Yes | Yes | Partial | Yes |
| Quotas | Yes | Plans | Plans | Plans | Yes | Account-level |
| HA Support | Yes | N/A | Yes | Yes | Yes | N/A |
| License | Apache 2.0 | Proprietary | Varies | MIT | Various | Proprietary |
Architecture
┌──────────────────────────────────────────────────────────┐│ Harbor Components ││ ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ ││ │ Portal │ │ Core │ │ Job │ │ Trivy │ ││ │ (Nginx) │──│ Service │──│ Service │──│Scanner │ ││ │ Web UI │ │ API │ │ Async │ │ │ ││ └──────────┘ └──────────┘ └──────────┘ └────────┘ ││ │ │ │ ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ Registry │ │PostgreSQL│ │ Redis │ ││ │ (Docker) │ │ (DB) │ │ (Cache) │ ││ └──────────┘ └──────────┘ └──────────┘ ││ │ ││ ┌──────────────────────────────────────┐ ││ │ Storage Backend │ ││ │ S3 | GCS | Azure | Swift | Local │ ││ └──────────────────────────────────────┘ │└──────────────────────────────────────────────────────────┘Installation
Helm on Kubernetes
# Add Harbor Helm repohelm repo add harbor https://helm.goharbor.iohelm repo update
# Create namespacekubectl create namespace harborexpose: type: ingress tls: enabled: true certSource: secret secret: secretName: harbor-tls ingress: hosts: core: registry.example.com className: nginx annotations: nginx.ingress.kubernetes.io/proxy-body-size: "0" nginx.ingress.kubernetes.io/ssl-redirect: "true"
externalURL: https://registry.example.com
persistence: enabled: true persistentVolumeClaim: registry: storageClass: fast-ssd size: 200Gi database: storageClass: fast-ssd size: 10Gi redis: storageClass: fast-ssd size: 5Gi trivy: storageClass: fast-ssd size: 10Gi imageChartStorage: type: s3 s3: region: eu-west-1 bucket: harbor-registry-storage accesskey: <ACCESS_KEY> secretkey: <SECRET_KEY>
harborAdminPassword: <ADMIN_PASSWORD>
database: type: internal internal: resources: requests: cpu: 500m memory: 512Mi
redis: type: internal
trivy: enabled: true gitHubToken: <GITHUB_TOKEN> severity: "HIGH,CRITICAL" autoRefresh: true
core: replicas: 2 resources: requests: cpu: 500m memory: 512Mi
jobservice: replicas: 2 resources: requests: cpu: 500m memory: 512Mi
registry: replicas: 2 resources: requests: cpu: 500m memory: 512Mi
portal: replicas: 2
metrics: enabled: true serviceMonitor: enabled: truehelm install harbor harbor/harbor \ --namespace harbor \ -f harbor-values.yamlDocker Compose (Offline Installer)
# Download offline installerwget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-offline-installer-v2.10.0.tgztar -xzf harbor-offline-installer-v2.10.0.tgzcd harbor
# Configurecp harbor.yml.tmpl harbor.yml# Edit harbor.yml with your settings
# Install./install.sh --with-trivyConfiguration
Project Management
# Login to Harbordocker login registry.example.com
# Create a project via APIcurl -X POST https://registry.example.com/api/v2.0/projects \ -H "Content-Type: application/json" \ -u "admin:Harbor12345" \ -d '{ "project_name": "production", "public": false, "storage_limit": 107374182400, "metadata": { "auto_scan": "true", "prevent_vul": "true", "severity": "critical" } }'LDAP/OIDC Integration
# harbor.yml OIDC configurationauth_mode: oidc_authoidc_name: "Keycloak"oidc_endpoint: "https://keycloak.example.com/realms/harbor"oidc_client_id: "harbor"oidc_client_secret: "<CLIENT_SECRET>"oidc_groups_claim: "groups"oidc_admin_group: "harbor-admins"oidc_scope: "openid,profile,email,groups"oidc_auto_onboard: trueoidc_user_claim: "preferred_username"Replication Rules
# Create replication endpointcurl -X POST https://registry.example.com/api/v2.0/registries \ -H "Content-Type: application/json" \ -u "admin:Harbor12345" \ -d '{ "name": "docker-hub", "type": "docker-hub", "url": "https://hub.docker.com", "credential": { "type": "basic", "access_key": "username", "access_secret": "password" } }'
# Create pull-based replication rulecurl -X POST https://registry.example.com/api/v2.0/replication/policies \ -H "Content-Type: application/json" \ -u "admin:Harbor12345" \ -d '{ "name": "replicate-from-hub", "src_registry": {"id": 1}, "dest_namespace": "library", "filters": [ {"type": "name", "value": "nginx"}, {"type": "tag", "value": "1.25*"} ], "trigger": { "type": "scheduled", "trigger_settings": {"cron": "0 0 * * *"} }, "enabled": true }'Proxy Cache
# Create proxy cache projectcurl -X POST https://registry.example.com/api/v2.0/projects \ -H "Content-Type: application/json" \ -u "admin:Harbor12345" \ -d '{ "project_name": "dockerhub-proxy", "registry_id": 1, "public": true }'# Pull through proxy cachedocker pull registry.example.com/dockerhub-proxy/library/nginx:1.25Robot Accounts for CI/CD
# Create robot accountcurl -X POST https://registry.example.com/api/v2.0/robots \ -H "Content-Type: application/json" \ -u "admin:Harbor12345" \ -d '{ "name": "ci-pipeline", "duration": -1, "level": "project", "permissions": [ { "namespace": "production", "kind": "project", "access": [ {"resource": "repository", "action": "push"}, {"resource": "repository", "action": "pull"}, {"resource": "artifact", "action": "read"}, {"resource": "scan", "action": "create"} ] } ] }'Image Scanning and Security
Automatic Scanning
# Enable auto-scan on push (project level)curl -X PUT https://registry.example.com/api/v2.0/projects/1 \ -H "Content-Type: application/json" \ -u "admin:Harbor12345" \ -d '{ "metadata": { "auto_scan": "true", "prevent_vul": "true", "severity": "critical" } }'
# Manually scan an artifactcurl -X POST https://registry.example.com/api/v2.0/projects/production/repositories/myapp/artifacts/sha256:abc123/scan \ -u "admin:Harbor12345"
# Get scan resultscurl https://registry.example.com/api/v2.0/projects/production/repositories/myapp/artifacts/sha256:abc123/additions/vulnerabilities \ -u "admin:Harbor12345"Image Signing with Cosign
# Generate key paircosign generate-key-pair
# Sign an imagecosign sign --key cosign.key registry.example.com/production/myapp:v1.0
# Verify signaturecosign verify --key cosign.pub registry.example.com/production/myapp:v1.0
# Configure Harbor to require signed images# Via project settings: enable "Content Trust" with CosignGarbage Collection
# Trigger garbage collection via APIcurl -X POST https://registry.example.com/api/v2.0/system/gc/schedule \ -H "Content-Type: application/json" \ -u "admin:Harbor12345" \ -d '{ "schedule": { "type": "Weekly", "cron": "0 0 0 * * 0" }, "parameters": { "delete_untagged": true, "dry_run": false } }'Monitoring
Prometheus Metrics
# Harbor ServiceMonitorapiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: harbor namespace: harborspec: selector: matchLabels: app: harbor endpoints: - port: metrics interval: 30sKey Metrics
| Metric | Description |
|---|---|
harbor_project_total | Total number of projects |
harbor_repository_total | Total number of repositories |
harbor_artifact_pulled | Number of image pulls |
harbor_artifact_pushed | Number of image pushes |
harbor_quotas_usage_bytes | Storage usage per project |
harbor_system_info | System health information |
Production Best Practices
Checklist
- Use S3-compatible object storage for registry backend
- Enable TLS with valid certificates
- Configure OIDC/LDAP for authentication
- Enable auto-scan on push for all projects
- Set vulnerability prevention policies (block critical/high)
- Configure image signing with Cosign
- Set up replication for disaster recovery
- Use proxy cache to reduce Docker Hub rate limits
- Create robot accounts for CI/CD (not user credentials)
- Enable audit logging for compliance
- Set storage quotas per project
- Schedule weekly garbage collection
- Deploy Harbor with HA (multiple replicas)
- Monitor with Prometheus and set alerts
- Regular backup of Harbor database and configuration
- Use tag retention policies to manage image lifecycle
Secure Your Container Supply Chain
Building a secure container registry requires expertise in image scanning, signing, RBAC, and registry operations. At chavkov.com, I deliver hands-on Harbor and container security training for production environments.
Contact me to discuss training options for your team.