Docker Hub's rate limits and basic GitLab registries push many DevOps engineers to self-host Harbor container registry instead. This guide shows you how to deploy Harbor with Docker Compose, secured with TLS, persistent storage, RBAC, and image replication. Also, you will learn how to enable Trivy scanning and integrate it with your Gitea CI/CD pipeline.
Self-Host Harbor Registry with Docker Compose, TLS, and Trivy
Table of Contents
- What Is Harbor and Why Self-Host It
- Prerequisites to Self-Host Harbor Container Registry
- Step 1. Update the Server and Install Docker
- Step 2. Download the Harbor Installer
- Step 3. Generate TLS Certificates
- Step 4. Set Up harbor.yml for Production
- Step 5. Run the Harbor Installer with Trivy Enabled
- Step 6. Verify TLS and Persistent Storage
- Step 7. Configure Project-Based RBAC
- Step 8. Set Up Image Replication Rules
- Step 9. Verify Trivy Vulnerability Scanning
- Step 10. Connect Harbor to Your Gitea CI/CD Pipeline
- Harbor Production Hardening Checklist
- Conclusion

What Is Harbor and Why Self-Host It
Harbor is a CNCF, open-source registry that adds a web UI, RBAC, image replication, audit logs, and built-in Trivy scanning on top of plain Docker Registry. Choosing to self-host Harbor container registry gives you full control over storage, network, and compliance with no SaaS fees.
Unlike Docker Hub's rate limits or a basic GitLab registry, Harbor offers per-project permissions, automatic CVE scanning on every push, and native cross-region replication.
Prerequisites to Self-Host Harbor Container Registry
Before you begin to self-host Harbor container registry, make sure your server meets these requirements:
- A Linux host running Ubuntu 24.04 or newer with at least 4 vCPUs, 8 GB RAM, and 40 GB of free disk space.
- Root or sudo access to the server.
- A registered domain name pointing to the server's public IP.
For heavy compute, a dedicated server with NVMe storage keeps image pull and push times fast, especially with multiple CI runners hitting the registry at once.
Step 1. Update the Server and Install Docker
The first step is to install Docker and Docker Compose on your server:
Add your user to the docker group so you don't need sudo for every command:
Step 2. Download the Harbor Installer
Harbor ships an offline installer bundle that generates the docker-compose.yml file for you based on your configuration. This is the officially supported way to self-host Harbor container registry with Docker Compose.
To download the Harbor installer, run the commands below:
Always check the Harbor releases page for the latest stable tag.
Step 3. Generate TLS Certificates
Production deployments should never run over plain HTTP. If you already own a certificate from Let's Encrypt or another CA, skip straight to placing the files.
If you don't have one, generate a self-signed CA and server certificate with OpenSSL:
If you use a Let's Encrypt certificate instead, run Certbot in standalone mode and point harbor.yml at the fullchain.pem and privkey.pem files it generates. This avoids trust warnings entirely.
Also, make the Docker daemon trust the certificate too, so docker login and docker push work without --insecure-registry:
Step 4. Set Up harbor.yml for Production
From the harbor directory, copy the template configuration file and edit it. This is the central file that controls how you self-host Harbor container registry, including hostname, TLS paths, admin password, database, and Trivy settings:
Update the following key fields:
The data_volume path is where all persistent storage for images, the database, and Redis lives on disk, so make sure that path sits on your fastest and largest disk. This is the most important setting for anyone planning to self-host Harbor container registry at scale.
Step 5. Run the Harbor Installer with Trivy Enabled
Run the installer with the --with-trivy flag so the built-in vulnerability scanner is deployed as an additional container alongside Harbor's core services:
The script will validate your configuration and generate the final docker-compose.yml from harbor.yml. It pulls all required images, creates the Docker network and named volumes for persistent storage, and starts every Harbor component, including:

Confirm every container is healthy:
At this point, you have a working environment on which to self-host Harbor container registry. Reach it from your browser:
Log in with username admin and the password you set in harbor_admin_password.

You should see the Harbor dashboard:

Step 6. Verify TLS and Persistent Storage
Confirm the certificate is being served correctly with the commands below:
Check that data actually persists across container restarts, which matters a lot once you self-host Harbor container registry for real workloads rather than a quick test:
Your images, projects, and users should still be present after this restart cycle because everything is stored under /data on the host, not inside the containers.
Step 7. Configure Project-Based RBAC
Harbor organizes images into projects, and each project has its own access control list. From the web UI:
Go to Projects > New Project, name it, for example, production, and choose Private unless you want public pulls.
- Project Name: production
- Access Level > Public: leave this unchecked
- Project quota limits: leave -1 (unlimited)
- Proxy Cache: leave off

Open the project, go to the Members tab, and click + User to assign roles
- Name: type the exact Harbor username of the person you want to add. They must already have a Harbor account; go to Administration > Users first if they don't exist yet.
- Role: pick based on what that person should do:
- Developer: for most engineers who just need to push/pull images.
- Maintainer: for leads who also need to trigger scans or tweak project config.
- Guest: read-only, pull access only.
- Limited Guest: read-only but can't see logs.
- Project Admin: full control; use carefully only for you or another admin.

Repeat for every team or environment that needs isolated access.
This project-based RBAC is a big reason teams self-host Harbor container registry instead of using a flat namespace like Docker Hub, where per-image permissions are much harder to enforce.
Note: For automated systems such as CI/CD pipelines, create a Robot Account instead of using a human account:
Grant it only push and pull permissions scoped to that project, then copy the generated token immediately; Harbor shows it only once.
Step 8. Set Up Image Replication Rules
Replication lets Harbor automatically copy images to or from another registry, which is useful for multi-region deployments or disaster recovery.
- Go to Administration > Registries > New Endpoint, and enter the target registry's URL, provider type, and credentials.
- Go to Administration > Replications > New Replication Rule.
- Choose the source project, the destination endpoint, and a trigger mode: Manual, Scheduled (cron), or Event-Based (triggers automatically on every push).
- Optionally filter by repository name pattern or tag pattern so only specific images replicate.
Event-based replication is the most common choice when you self-host Harbor container registry across two data centers; it keeps a backup copy in sync automatically.
Step 9. Verify Trivy Vulnerability Scanning
Because you installed Harbor with --with-trivy, the scanner is already registered. Verify it and turn on automatic scanning:
Log in as admin, go to Administration > Interrogation Services > Scanners, and confirm Trivy shows a Healthy status.

- Open any project, go to its Configuration tab, and enable Automatically scan images on push so every new image is scanned the moment it lands in the registry.
- Optionally enable Prevent vulnerable images from running and set a severity threshold, for example, block anything rated Critical, to stop compromised images from ever being pulled.
- To scan everything already stored, go to Administration > Interrogation Services > Vulnerability, click Scan Now, or set up a recurring schedule with Schedule to scan all.

Push a test image and check its scan report to confirm the pipeline works correctly:
Open the image in the Harbor UI under Projects > production > Repositories and click the tag to view its Trivy CVE report, broken down by severity.
Step 10. Connect Harbor to Your Gitea CI/CD Pipeline
Now that Harbor is running with TLS, RBAC, and scanning, you can connect it as the private registry target for your Gitea Actions or Gitea CI pipeline. If you haven't set up Gitea's CI/CD yet, follow our Gitea self-hosted CI/CD with Docker Compose guide first, then come back to finish this integration.
In Gitea, go to the repository's Settings > Secrets, and add HARBOR_USERNAME and HARBOR_PASSWORD (use the robot account token you created in Step 7, not the admin password).
In your Gitea Actions workflow file (.gitea/workflows/build.yml), add a login and push step:
Every push now triggers a build, a push to Harbor, and an automatic Trivy scan, giving you a complete build-scan-store loop without touching Docker Hub.
With this in place, Gitea handles source control and automation, while Harbor is the trusted, scanned image store your pipeline pushes to, and your servers pull from.
Harbor Production Hardening Checklist
Before you consider this deployment done, review this checklist:
- Rotate
harbor_admin_passwordand database passwords immediately after first login. - Use a real CA-signed certificate instead of a self-signed one for anything internet-facing.
- Put Harbor's
/datadirectory on fast NVMe storage. - Enable Harbor's built-in garbage collection on a schedule to reclaim space from deleted image layers.
- Back up the PostgreSQL volume and
/data/certdirectory regularly; losing the certificate breaks every client that pinned the CA. - Restrict inbound access to the Harbor host with a firewall or VPN if it doesn't need to be publicly reachable.
If you're planning to self-host Harbor container registry for a team, disk and network speed matter most, because Harbor is I/O-heavy, and Trivy adds extra load. A PerLod dedicated server with NVMe storage keeps pull/push times low under heavy CI traffic, with full root access to tune everything yourself.
Conclusion
At this point, you have learned to self-host Harbor container registry with Docker Compose, TLS, persistent storage, RBAC, replication, and Trivy scanning. Connected to Gitea CI/CD, Harbor becomes your team's single source of truth for images, a private, secure registry you fully control instead of Docker Hub.
We hope you enjoy this guide.
Yes, Trivy is optional, but enabling it costs nothing and adds automated vulnerability scanning on every image.
It depends on image count and size, but plan for at least 40 to 100 GB to start, and monitor growth since every pushed layer and its scan cache consume storage.
Yes, use Harbor's replication feature to pull images from Docker Hub into a project, or docker pull, retag, and docker push them manually.