Introduction
Harbor gives you:
- Private Docker/OCI registry (vs public Docker Hub)
- Automatic vulnerability scanning (integrated Trivy)
- Image signing with Cosign / Notary
- Multi-site replication
- RBAC: users, groups, projects
- Retention policies (auto-cleanup old images)
- Webhooks for CI/CD
- Helm charts hosting
- Garbage collection
Alternative to: Docker Hub (expensive), GitLab Registry, AWS ECR, Azure ACR.
Prerequisites
- Linux VPS Debian/Ubuntu (4 vCPU, 8 GB RAM, 200+ GB SSD)
- Docker + Docker Compose
- Domain name
- TLS certificate (Let's Encrypt)
Step 1: Download Harbor
cd /opt
sudo wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-online-installer-v2.10.0.tgz
sudo tar xzf harbor-online-installer-v2.10.0.tgz
cd harbor
Step 2: Generate TLS cert
Let's Encrypt via certbot DNS-01:
sudo certbot certonly --manual --preferred-challenges=dns -d registry.your-domain.com
Copy:
sudo mkdir -p /etc/harbor/certs
sudo cp /etc/letsencrypt/live/registry.your-domain.com/fullchain.pem /etc/harbor/certs/
sudo cp /etc/letsencrypt/live/registry.your-domain.com/privkey.pem /etc/harbor/certs/
Step 3: Configure harbor.yml
sudo cp harbor.yml.tmpl harbor.yml
sudo nano harbor.yml
hostname: registry.your-domain.com
http:
port: 80
https:
port: 443
certificate: /etc/harbor/certs/fullchain.pem
private_key: /etc/harbor/certs/privkey.pem
harbor_admin_password: strong-password
database:
password: strong-db-password
max_idle_conns: 100
max_open_conns: 900
data_volume: /data/harbor
trivy:
ignore_unfixed: false
skip_update: false
insecure: false
log:
level: info
local:
rotate_count: 50
rotate_size: 200M
location: /var/log/harbor
Step 4: Install
sudo ./install.sh --with-trivy
Takes 5-10 min. Downloads images, configures docker-compose, starts stack.
Step 5: First access
Visit https://registry.your-domain.com. Login: admin + your password.
Step 6: Create a project
Projects > New project. Name: myteam. Access: Private.
Step 7: Push an image
docker login registry.your-domain.com
docker pull alpine:latest
docker tag alpine:latest registry.your-domain.com/myteam/alpine:latest
docker push registry.your-domain.com/myteam/alpine:latest
Image appears in Harbor UI under myteam.
Step 8: Vulnerability scan
Click image > Scan. Trivy scans and lists CVEs.
For auto-scan on every push:
Project > Configuration > Vulnerability scanning > Automatically scan images on push.
Step 9: Retention policies
Project > Policy > Tag Retention. Example:
- Keep last 10 versions per tag
- Except
latest,stable,release-*
Schedule daily 2am:
Project > Policy > Tag Retention > Schedule > Custom: 0 2 * * *.
Step 10: Replication
To replicate to another Harbor / Docker Hub / ECR:
Administration > Registries > New endpoint.
Administration > Replications > New rule. Source > destination.
E.g.: replicate all release-* to AWS ECR for DR.
Step 11: Robot accounts for CI/CD
Project > Robot Accounts > New robot account.
Name: ci-bot. Permissions: push + pull. Token generated.
In CI:
echo "$TOKEN" | docker login registry.your-domain.com -u 'robot$myteam+ci-bot' --password-stdin
docker build -t registry.your-domain.com/myteam/myapp:$VERSION .
docker push registry.your-domain.com/myteam/myapp:$VERSION
Step 12: Garbage collection
Harbor keeps orphan layers when you delete images. GC cleans:
Administration > Garbage Collection > Schedule. E.g. every Sunday 3am.
Run manually first time: GC NOW.
⚠️ GC blocks pushes during execution.
Troubleshooting
"x509: certificate signed by unknown authority"
Docker client doesn't trust cert. For Let's Encrypt: OK. For self-signed:
sudo mkdir -p /etc/docker/certs.d/registry.your-domain.com
sudo cp ca.crt /etc/docker/certs.d/registry.your-domain.com/
sudo systemctl restart docker
"denied: requested access to the resource is denied"
Check:
- Project visibility (Public vs Private)
- User in project members
- Robot token still valid
Disk filling up
Too many images. Configure retention + GC. Check:
sudo du -sh /data/harbor/registry
Harbor down / slow
cd /opt/harbor
sudo docker compose ps
sudo docker compose logs -f core
sudo docker compose logs -f registry
Often: Postgres or Redis low on resources. Tune.
Trivy scan timeout
Increase timeout in harbor.yml:
trivy:
timeout: 30m
Reinstall:
sudo docker compose down
sudo ./install.sh --with-trivy
Useful commands
cd /opt/harbor
sudo docker compose ps
sudo docker compose logs -f
sudo docker compose restart
sudo docker compose down
sudo docker compose up -d
# Update Harbor (backup first!)
sudo docker compose down
sudo cp -r /opt/harbor /opt/harbor.bak
# Download new version, copy harbor.yml, ./install.sh
# Backup
sudo docker compose exec postgresql pg_dump -U postgres registry | gzip > harbor-db-$(date +%F).sql.gz
sudo tar czf harbor-data-$(date +%F).tar.gz /data/harbor
# Test pull/push
docker login registry.your-domain.com
docker pull registry.your-domain.com/library/alpine:latest
Conclusion
Harbor is the serious solution for in-house image hosting:
- Security: scan + signing + RBAC
- Productivity: webhooks, retention, replication
- Compatibility: Docker, OCI, Helm charts
Going further:
- Enable Cosign signing for supply chain security
- Combine with Falco for runtime security
- For large K8s clusters, configure replication to edge registry near nodes
Resources
- Official docs: https://goharbor.io/docs/
- Site: https://goharbor.io
- GitHub: https://github.com/goharbor/harbor
- Trivy: https://aquasecurity.github.io/trivy/

















