Skip to content
Logo
Table of contents
VeryCloud
DocumentationVPS Linux

Harbor: private Docker registry with scanning

Harbor: private Docker registry with scanning

Harbor is the CNCF open-source container registry: private Docker/OCI image storage, vulnerability scanning (Trivy), signing (Cosign), replication, RBAC. The professional solution to host your images in-house.

Category
VPS Linux
Level
Intermediate
Reading time
4 min
Published
May 17, 2026

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

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members