Install Harbor Registry on Kubernetes/OpenShift

Posted on 271 views

Harbor is an open-source cloud native registry that stores, signs, and scans container images for vulnerabilities. This guide will walk you through the installation of Harbor Image Registry on Kubernetes / OpenShift with Helm Chart. Some of the cool features of Harbor image registry are:

Features of Harbor Registry

  • Multi-tenant support
  • Security and vulnerability analysis support
  • Extensible API and web UI
  • Content signing and validation
  • ​Image replication across multiple Harbor instances
  • ​Identity integration and role-based access control

Helm is a command-line interface (CLI) tool that was created to simplify deployment of applications and services to Kubernetes / OpenShift Container Platform clusters. Helm uses a packaging format called charts. A Helm chart is a collection of files that describes Kubernetes resources.

OpenShift Courses:

Step 1: Install Helm 3 on Linux / macOS

Helm is distributed a binary application which means no dependency is required to install it on your Linux / macOS machine:

### Linux ###
sudo curl -L -o /usr/local/bin/helm
sudo chmod +x /usr/local/bin/helm

### macOS ###
sudo curl -L -o /usr/local/bin/helm
sudo chmod +x /usr/local/bin/helm

Check the installed version:

$ helm version
version.BuildInfoVersion:"v3.9.2", GitCommit:"1addefbfe665c350f4daf868a9adc5600cc064fd", GitTreeState:"clean", GoVersion:"go1.18.4"

Step 2: Install Harbor Helm Chart on Kubernetes / OpenShift

Chart is a Helm package. It contains all of the resource definitions necessary to run an application, tool, or service inside of a Kubernetes cluster.

Add Harbor Helm repository:

$ helm repo add harbor
"harbor" has been added to your repositories

Update the repository:

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "harbor" chart repository
Update Complete. ⎈Happy Helming!⎈

Configure the chart

The configuration items can be set via –set flag during installation or configured by editing the values.yaml directly.

You can download default values.yaml file.


You can modify the values in the file to suit your installation:

vim values.yaml

See Harbor Helm configuration page for accepted values in different blocks.

Once you’ve customize the file then install Harbor helm chart with custom configurations once done modifying.

$ helm install harbor harbor/harbor -f values.yaml -n harbor
NAME: harbor
LAST DEPLOYED: Wed Apr  1 19:20:07 2021
STATUS: deployed
Please wait for several minutes for Harbor deployment to complete.
Then you should be able to visit the Harbor portal at
For more details, please visit

You can as well --set to pass values during Helm installation. See Harbor Helm configuration page

Here is an example with few parameters passed with –set flag.

helm install harbor harbor/harbor \
--set persistence.persistentVolumeClaim.registry.accessMode=ReadWriteMany \
--set persistence.persistentVolumeClaim.registry.size=50Gi \
--set persistence.persistentVolumeClaim.chartmuseum.size=5Gi \
--set persistence.persistentVolumeClaim.database.size=5Gi \
--set externalURL= \
--set \
--set \
--set harborAdminPassword=[email protected] \
-n harbor

Check status to confirm it is deployed:

$ helm status harbor

Updating Hem deployment

If you update parameters in values.yml or add new ones, upgrade helm deployment with the command:

$ helm upgrade harbor harbor/harbor -f values.yml -n harbor
Release "harbor" has been upgraded. Happy Helming!
NAME: harbor
LAST DEPLOYED: Thu Apr 30 11:30:06 2021
STATUS: deployed
Please wait for several minutes for Harbor deployment to complete.
Then you should be able to visit the Harbor portal at
For more details, please visit

If you ever want to remove deployment, run:

$ helm uninstall  harbor  -n harbor

Fixing Init:CrashLoopBackOff on harbor-harbor-database-0 on OpenShift

Some container images such as postgres and redis require root access and have certain expectations about how volumes are owned. We need relax the security in the cluster so that images are not forced to run as a pre-allocated UID, without granting everyone access to the privileged SCC:

Grant all harbor authenticated user access to the anyuid SCC:

oc adm policy add-scc-to-user anyuid system:serviceaccount:harbor:default

Check your deployments status:

$ kubectl get deployments
NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
harbor-harbor-chartmuseum     1/1     1            1           24m
harbor-harbor-clair           1/1     1            1           24m
harbor-harbor-core            1/1     1            1           24m
harbor-harbor-jobservice      1/1     1            1           24m
harbor-harbor-notary-server   1/1     1            1           24m
harbor-harbor-notary-signer   1/1     1            1           24m
harbor-harbor-portal          1/1     1            1           24m
harbor-harbor-registry        1/1     1            1           24m

Check pod status:

$ kubectl get pods
NAME                                           READY   STATUS    RESTARTS   AGE
harbor-harbor-chartmuseum-58f8647f95-mtmmf     1/1     Running   0          5m16s
harbor-harbor-clair-654dcfd8bf-77qs6           2/2     Running   0          5m16s
harbor-harbor-core-5cb85989d6-r7s84            1/1     Running   0          5m16s
harbor-harbor-database-0                       1/1     Running   0          5m33s
harbor-harbor-jobservice-fc54cf784-lv864       1/1     Running   0          5m16s
harbor-harbor-notary-server-65d8fb7c77-xgxvg   1/1     Running   0          5m16s
harbor-harbor-notary-signer-66c9db4cf4-5bwvh   1/1     Running   0          5m16s
harbor-harbor-portal-5cbc6d5897-r5wzh          1/1     Running   0          25m
harbor-harbor-redis-0                          1/1     Running   0          5m16s
harbor-harbor-registry-7ff65976f4-sgnnd        2/2     Running   0          5m16s

Lastly confirm Services and ingress are created.

$ kubectl get svc
NAME                          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
harbor-harbor-chartmuseum     ClusterIP           80/TCP              26m
harbor-harbor-clair           ClusterIP           8080/TCP            26m
harbor-harbor-core            ClusterIP            80/TCP              26m
harbor-harbor-database        ClusterIP           5432/TCP            26m
harbor-harbor-jobservice      ClusterIP             80/TCP              26m
harbor-harbor-notary-server   ClusterIP           4443/TCP            26m
harbor-harbor-notary-signer   ClusterIP             7899/TCP            26m
harbor-harbor-portal          ClusterIP            80/TCP              26m
harbor-harbor-redis           ClusterIP           6379/TCP            26m
harbor-harbor-registry        ClusterIP           5000/TCP,8080/TCP   26m

$ kubectl get ing
NAME                    HOSTS                                     ADDRESS   PORTS     AGE
harbor-harbor-ingress   core.harbor.domain,notary.harbor.domain             80, 443   26m

Since I’m actually doing this deployment on OpenShift, I’ll have routes created.

$ kubectl get route
NAME                          HOST/PORT              PATH          SERVICES                      PORT   TERMINATION     WILDCARD
harbor-harbor-ingress-7f9vg   notary.harbor.domain   /             harbor-harbor-notary-server   4443   edge/Redirect   None
harbor-harbor-ingress-9pvvz   core.harbor.domain     /             harbor-harbor-portal          8080   edge/Redirect   None
harbor-harbor-ingress-d7mcn   core.harbor.domain     /c/           harbor-harbor-core            8080   edge/Redirect   None
harbor-harbor-ingress-gn5w6   core.harbor.domain     /chartrepo/   harbor-harbor-core            8080   edge/Redirect   None
harbor-harbor-ingress-jf48l   core.harbor.domain     /service/     harbor-harbor-core            8080   edge/Redirect   None
harbor-harbor-ingress-lhbx4   core.harbor.domain     /api/         harbor-harbor-core            8080   edge/Redirect   None
harbor-harbor-ingress-vtt8v   core.harbor.domain     /v2/          harbor-harbor-core            8080   edge/Redirect   None

A number of persistent volume claims are created as well. Matching the values of size you specified.

$ kubectl  get pvc
NAME                                     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS                AGE
data-harbor-harbor-redis-0               Bound    pvc-1de4a5b2-d55a-48cc-b8b6-1b258214260c   1Gi        RWO            ocs-storagecluster-cephfs   29m
database-data-harbor-harbor-database-0   Bound    pvc-9754adde-e2bd-40ee-b18b-d72eacfdfc12   1Gi        RWO            ocs-storagecluster-cephfs   29m
harbor-harbor-chartmuseum                Bound    pvc-3944fce8-ecee-4bec-b0f6-cc5da3b30572   5Gi        RWO            ocs-storagecluster-cephfs   29m
harbor-harbor-jobservice                 Bound    pvc-5ecf0be4-002c-4628-8dcc-283e996175bc   1Gi        RWO            ocs-storagecluster-cephfs   29m
harbor-harbor-registry                   Bound    pvc-072358e9-06f2-4384-b7d6-88e97eb29499   5Gi        RWO            ocs-storagecluster-cephfs   29m

Step 3: Access Harbor Administration dashboard

Use the external Domain configured during installation to access the Harbor container registry dashboard.

install-harbor-registry-02 (1)

Default logins if you didn’t change password are:

Username: admin
Password: Harbor12345

Don’t forget to change your password after first login.

Step 4: Add Pull Secret to Kubernetes / OpenShift

Follow the steps in the guide below to add pull secret to Kubernetes / OpenShift.


Gravatar Image
A systems engineer with excellent skills in systems administration, cloud computing, systems deployment, virtualization, containers, and a certified ethical hacker.