Harbor is an enterprise-grade open-source container registry that extends the basic Docker Registry with role-based access control, vulnerability scanning, image signing, and replication across multiple registries. While Docker’s official registry:2 image is suitable for simple use cases, Harbor is purpose-built for teams that need audit logs, project-level isolation, and automated security scanning before images reach production. Harbor integrates natively with Trivy for CVE scanning, Notary for image signing, and supports replication to AWS ECR, GCR, and other Harbor instances. This guide covers installing Harbor on RHEL 9 using Docker Compose and configuring it as a fully functional private registry.

Prerequisites

  • RHEL 9 server with at least 4 CPU cores and 8 GB RAM (Harbor + Trivy are resource-intensive)
  • Docker and Docker Compose installed and running
  • A domain name pointing to the server (e.g. harbor.example.com) — required for TLS
  • TLS certificate and private key for the domain (Let’s Encrypt or internal CA)
  • Ports 80 and 443 open in firewalld

Step 1 — Open Firewall Ports and Prepare Directories

# Open HTTP/HTTPS in firewalld
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

# Create directories for certificates and Harbor data
mkdir -p /opt/harbor/certs
mkdir -p /data/harbor

# Copy your TLS certificate and key into place
cp fullchain.pem /opt/harbor/certs/harbor.example.com.crt
cp privkey.pem   /opt/harbor/certs/harbor.example.com.key
chmod 600 /opt/harbor/certs/*.key

Step 2 — Download the Harbor Offline Installer

# Find the latest release at https://github.com/goharbor/harbor/releases
export HARBOR_VERSION=v2.10.2

cd /opt
curl -LO https://github.com/goharbor/harbor/releases/download/${HARBOR_VERSION}/harbor-offline-installer-${HARBOR_VERSION}.tgz

# Verify the checksum
curl -LO https://github.com/goharbor/harbor/releases/download/${HARBOR_VERSION}/harbor-offline-installer-${HARBOR_VERSION}.tgz.asc

# Extract the installer
tar xzf harbor-offline-installer-${HARBOR_VERSION}.tgz
cd /opt/harbor

Step 3 — Configure harbor.yml

# Copy the template and edit the required fields
cp harbor.yml.tmpl harbor.yml

# Key fields to configure in harbor.yml:
#
# hostname: harbor.example.com
#
# http:
#   port: 80
#
# https:
#   port: 443
#   certificate: /opt/harbor/certs/harbor.example.com.crt
#   private_key: /opt/harbor/certs/harbor.example.com.key
#
# harbor_admin_password: YourStrongAdminPassword
#
# data_volume: /data/harbor
#
# trivy:
#   ignore_unfixed: false
#   skip_update: false
#   offline_scan: false

# Edit the file with your actual values
vi harbor.yml

Step 4 — Run the Harbor Installer

# The --with-trivy flag enables the built-in vulnerability scanner
cd /opt/harbor
./install.sh --with-trivy

# Verify all Harbor containers are running
docker compose ps

# Expected containers:
# harbor-core, harbor-db, harbor-jobservice, harbor-log,
# harbor-portal, nginx, redis, registry, registryctl, trivy-adapter

Step 5 — Create Projects, Users, and Push Images

# 1. Log in to the Harbor UI at https://harbor.example.com
#    Username: admin   Password: (from harbor.yml)
#
# 2. Create a new project named "myproject" (Projects → New Project)
#    Set access level to Private
#
# 3. Create a robot account for CI/CD (Administration → Robot Accounts)
#    or create a regular user (Administration → Users)

# 4. Configure Docker on your client to trust the registry
#    (skip if using a publicly trusted TLS certificate)
mkdir -p /etc/docker/certs.d/harbor.example.com
cp /opt/harbor/certs/harbor.example.com.crt 
   /etc/docker/certs.d/harbor.example.com/ca.crt
systemctl restart docker

# 5. Log in, tag, and push an image
docker login harbor.example.com -u admin -p YourStrongAdminPassword

docker pull nginx:latest
docker tag  nginx:latest harbor.example.com/myproject/nginx:latest
docker push harbor.example.com/myproject/nginx:latest

# 6. Pull the image on another host
docker pull harbor.example.com/myproject/nginx:latest

Step 6 — Enable Vulnerability Scanning and Image Replication

# Trigger a manual vulnerability scan on a pushed image:
# Harbor UI → myproject → Repositories → nginx → Artifacts → Scan

# Configure auto-scan on push:
# Harbor UI → myproject → Configuration → Enable content trust
#   → Automatically scan images on push → Save

# Set up replication to a remote registry:
# Administration → Registries → New Endpoint
#   Provider: Harbor (or AWS ECR, Docker Hub, etc.)
#   Endpoint URL: https://harbor2.example.com
#
# Administration → Replications → New Replication Rule
#   Replication mode: Push-based
#   Source: harbor.example.com/myproject/**
#   Destination: harbor2.example.com/backup-project
#   Trigger: Event-based (on push)

Conclusion

Harbor transforms a basic container registry into a fully governed image distribution platform with RBAC, vulnerability scanning, and cross-registry replication. By requiring every pushed image to pass a Trivy CVE scan before it can be promoted to production, teams can shift security left and catch vulnerabilities before they are deployed. The project-level isolation allows multiple teams to share a single Harbor instance without access bleed. For production deployments, configure Harbor with an external PostgreSQL database and Redis cluster for high availability, schedule automated vulnerability database updates, and integrate the robot account credentials into your CI/CD pipeline as secrets rather than hardcoding credentials.

Next steps: How to Install and Configure Drone CI on RHEL 9, How to Configure Prometheus AlertManager on RHEL 9, and How to Build and Push Docker Images in CI/CD Pipelines on RHEL 9.