Harbor 2
Overview
Harbor is the CNCF graduated open source container and OCI artifact registry. Pinterest, Trend Micro, Tencent and JD.com use the same project for their private, on-premises registry needs.
The SecureAnyCloud image installs Harbor 2.15.0 from the official offline installer. It runs as a Docker Compose v2 stack of eight services, orchestrated by the official docker-compose.yml and fronted by the bundled nginx container of Harbor.
A per-VM admin password is generated at first boot. The Harbor portal is reachable as soon as the VM finishes booting, and Docker login works immediately for image push and pull.
What you get:
- Harbor 2.15.0 (Apache 2.0, CNCF graduated) installed from the official offline installer
- Docker CE 29.4.2 and the Docker Compose v2 plugin v5.1.3, from the noble APT repository of Docker
- Eight Harbor containers:
harbor-core,harbor-portal(Angular UI),harbor-jobservice,harbor-db(PostgreSQL 13),redis,registry(CNCF distribution),registryctlandnginx(the nginx bundled with Harbor, not the system nginx) - RBAC, project-level access control, robot accounts, replication rules, image signing (Cosign), webhooks, retention policies and garbage collection
- A per-VM Harbor admin password (32 hex chars), rotated at first boot
- A per-VM Postgres password (32 hex chars), rotated at first boot
- Hostname auto-detected from the Azure IMDS public IP at first boot
- Two systemd units:
harbor.service(a oneshot wrappingdocker compose up -d) andharbor-firstboot.service(Before=harbor.service) - The Trivy vulnerability scanner is not installed by default, to fit the Standard_B2s memory budget. Opt in with
./install.sh --with-trivyon D2s_v5 or larger - 24/7 SecureAnyCloud support
Prerequisites
You need an active Azure subscription, an SSH key, and a VNet with a subnet.
Standard_B2s (4 GB RAM) is the supported minimum. It is comfortable for the eight Harbor containers in the default layout without Trivy. Choose Standard_D2s_v5 (8 GB) or larger if you plan to opt in to Trivy (see Step 12).
For NSG inbound rules, allow 22/tcp from your management CIDR. Allow 80/tcp from any client CIDR that needs the Harbor portal and Docker registry. Allow 443/tcp once you front Harbor with TLS in Step 13.
Step 1-3: Deploy and connect over SSH
Deploy the SecureAnyCloud Harbor 2 image from the Azure Marketplace with your SSH key, then SSH to the VM as azureuser:
ssh azureuser@<vm-ip>Check service status and versions
Check the services, the Docker versions and the containers:
sudo systemctl is-active docker.service harbor.service harbor-firstboot.service docker --version docker compose version
sudo docker ps --format "table {{.Names}}\t{{.Status}}"You should see active three times, Docker CE 29.x, Compose v2.x, and all eight Harbor containers in the Up state.
Read the per-VM credentials
Read the credentials file:
sudo cat /stage/scripts/harbor-credentials.logNote HARBOR_URL, HARBOR_UI_USER (always admin) and HARBOR_ADMIN_PASSWORD. The same file also contains HARBOR_DB_PASSWORD (the PostgreSQL password inside the harbor-db container) and a ready-to-paste HARBOR_DOCKER_LOGIN line.
Check API health and version
Query the health and system information endpoints:
curl -sf http://127.0.0.1/api/v2.0/health | jq
curl -sf http://127.0.0.1/api/v2.0/systeminfo | jq '{harbor_version, registry_url, auth_mode}'The first call hits the anonymous health endpoint of Harbor. It should return "status":"healthy" along with the status of each component: core, portal, database, redis, registry, registryctl and jobservice.
The second call prints the Harbor version (v2.15.0) and the registry URL.
Sign in to the Harbor web UI
Browse to http://<vm-ip>/ and sign in with admin and the password from Step 5.
Explore the Projects dashboard
After you sign in, the Projects page is the default view. The pre-created library project is public by default. Push images there without creating a new project, or click NEW PROJECT to add a private one.
Review system administration
Click Administration in the left sidebar. From there you can reach Users, Robot Accounts, Registries (replication targets), Replications, Labels, Project Quotas, Interrogation Services and Configuration.
Push a container image
From any machine that has Docker installed, log in to your Harbor instance, tag a sample image and push it to the library project. Replace <vm-ip> and <password> with the values from Step 5.
docker login <vm-ip> -u admin -p '<password>'
docker pull busybox:latest
docker tag busybox:latest <vm-ip>/library/busybox:latest
docker push <vm-ip>/library/busybox:latestRefresh the library project in the Harbor UI. The busybox repository now appears with a manifest and one tag.
Pull from Harbor
Pull the image back from a second machine, or from the same machine after you run docker rmi <vm-ip>/library/busybox:latest:
docker login <vm-ip> -u admin -p '<password>'
docker pull <vm-ip>/library/busybox:latest
docker run --rm <vm-ip>/library/busybox:latest echo "pulled from sac harbor"For unauthenticated pulls from public projects, skip the docker login step.
Add the Trivy vulnerability scanner
Trivy is the CNCF reference vulnerability scanner for OCI images. It is omitted from the default install to fit the Standard_B2s RAM budget. Enabling it adds approximately 500 MB of resident memory for the Trivy adapter container, plus its CVE database cache.
Resize the VM to Standard_D2s_v5 (8 GB) or larger, then run the installer with Trivy enabled:
cd /opt/harbor
sudo ./install.sh --with-trivyThe installer brings the stack down, recreates docker-compose.yml with the Trivy service, and brings the stack back up.
When it returns, open Administration > Interrogation Services in the Harbor UI to confirm that Trivy is registered. Then open Projects > library > Configuration > Vulnerability scanning to enable automatic scans on push.
Add your domain (TLS with certbot)
There are two production-ready options for adding HTTPS to Harbor.
Option A: Reconfigure the bundled nginx of Harbor for TLS. Edit /opt/harbor/harbor.yml and uncomment the https: block. Set the certificate: and private_key: paths to your PEM files and set hostname: to your FQDN. Then re-run the installer:
sudo nano /opt/harbor/harbor.yml cd /opt/harbor
sudo ./install.shOption B: Front Harbor with the system nginx and certbot (recommended). Point your DNS A record at this VM, then install nginx and certbot:
sudo apt-get update
sudo apt-get install -y nginx python3-certbot-nginxAdd a server block at /etc/nginx/sites-available/harbor-sac that proxies to http://127.0.0.1:80, the bundled nginx of Harbor. Enable the site, then run certbot:
sudo ln -s /etc/nginx/sites-available/harbor-sac /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d harbor.example.com -m you@example.com --agree-tos --non-interactive --redirectFinally, set hostname: harbor.example.com in /opt/harbor/harbor.yml and re-run sudo ./install.sh, so the metadata of Harbor advertises the public FQDN.
Configure replication
Harbor can mirror images to and from Docker Hub, Amazon ECR, Azure ACR, GitHub GHCR, Quay, GitLab and other Harbor instances. In the UI:
- Open Administration > Registries > NEW ENDPOINT. Pick a provider, enter the URL and access credentials, then click TEST CONNECTION and OK.
- Open Administration > Replications > NEW REPLICATION RULE. Choose Push-based or Pull-based, set the source and destination filters and a trigger (manual, scheduled, or event-based on push), then click SAVE.
- Trigger the rule manually from the Replications list to validate it, then leave the schedule running.
Back up Harbor
You can take a consistent backup while the registry is running. Dump the Harbor PostgreSQL database inside the harbor-db container with pg_dump, and add a tarball of the /data/registry directory (the image blobs):
sudo mkdir -p /var/backups/harbor
sudo docker exec harbor-db pg_dumpall -U postgres | gzip | sudo tee /var/backups/harbor/harbor-db-$(date +%F).sql.gz > /dev/null
sudo tar czf /var/backups/harbor/harbor-registry-$(date +%F).tgz -C /data registryPeriodically copy /var/backups/harbor to Azure Blob Storage (az storage blob upload-batch) for off-VM retention.
To restore on a fresh VM, stop Harbor (sudo systemctl stop harbor) and run gunzip | docker exec -i harbor-db psql -U postgres. Then untar /data/registry and start Harbor with sudo systemctl start harbor.
Logs and troubleshooting
Read the service journals and the container logs:
sudo journalctl -u harbor.service --no-pager -n 80
sudo journalctl -u harbor-firstboot.service --no-pager -n 80
sudo docker compose -f /opt/harbor/docker-compose.yml logs --tail 50
sudo docker logs harbor-core --tail 50
sudo docker logs harbor-jobservice --tail 50
sudo docker logs harbor-db --tail 50harbor.service is a oneshot wrapper around docker compose -f /opt/harbor/docker-compose.yml up -d / down. Its journal entries describe stack-level lifecycle events. Application-level logs live inside each container and are available through docker logs.
The bundled nginx container writes the web access logs of the portal. Run sudo docker logs nginx --tail 100 to see the HTTP request lines.
Security
- A per-VM Harbor admin password (32 hex chars) is rotated at first boot.
- A per-VM Postgres password (32 hex chars) is rotated at first boot.
- RBAC is enforced on every API call.
/api/v2.0/users/currentreturns 401 without credentials. - Projects support public and private visibility. The default
libraryproject is public, and new projects are private by default. - Robot accounts for CI/CD have scoped permissions, so no human admin password is needed in pipelines.
- Optional Cosign content trust signing is available. Enable it per project under Configuration > Deployment security.
- The NSG opens only 80/tcp (HTTP) and, optionally, 443/tcp (TLS through Step 13).
- The bundled nginx of Harbor terminates HTTP on port 80, and TLS is off by default. For production, front it with the system nginx and certbot (Step 13).
- PostgreSQL and Redis listen only on the private Docker bridge network and are never exposed on the host.
- Optional Trivy vulnerability scanning can be enabled per project to block image push when CVEs above a threshold are detected (Step 12).
Support
SecureAnyCloud provides 24/7/365 expert technical support, with a guaranteed response within 24 hours and a one hour average for critical issues. Contact sales@secureanycloud.com.
Deploy on Azure
Launch Harbor 2.15.0 Container Registry on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com