[{"data":1,"prerenderedAt":205},["ShallowReactive",2],{"seo-verification":3,"blog-self-host-harbor-registry-on-a-vps-complete-guide-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-self-host-harbor-registry-on-a-vps-complete-guide-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":27,"featuredImage":29,"bgImage":30,"posterImage":31,"relatedSolution":29,"intro":32,"sections":33,"ctaTitle":150,"ctaBody":151,"ctaButton":152,"ctaUrl":153,"relatedPosts":154},439,"self-host-harbor-registry-on-a-vps-complete-guide",{"fr":12,"en":10,"ar":13,"es":14},"self-host-harbor-registry-vps","استضافة-harbor-على-vps-سجل-حاويات-docker-خاص","harbor-registry-self-hosted-en-vps-guia-completa","Self-host Harbor Registry on a VPS: complete guide","Deploy Harbor v2.15 on your VPS: private Docker registry with Trivy scanning, Cosign signing, RBAC and NIS2\u002FGDPR compliance. Step-by-step guide.",8,0,false,"2026-10-13T00:00:00+00:00","2026-10-13T14:10:28+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":25},3,"Deployment","deploiement","bg-success\u002F10 text-success",[28],{"id":23,"name":24,"slug":25,"color":26,"icon":25},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fself-host-harbor-registry-vps-poster.svg","Since CVE-2026-21589 shook the Atlassian supply chain, DevOps teams are looking to regain control over every artifact that flows through their pipeline. Docker Hub enforces a 100 pulls\u002F6h rate limit on free accounts, GHCR ties your images to GitHub's retention policy, and neither signs nor scans images by default. Harbor, the CNCF open source registry, solves all three problems in a single deployment: integrated Trivy vulnerability scanning, Cosign signing, granular RBAC and complete audit logs. This guide takes you from Docker Compose installation to production admission policies, on a 4 vCPU \u002F 8 GB RAM VPS.",[34,38,49,52,77,80,83,87,90],{"type":35,"title":36,"body":37},"h2","Why a private Docker registry with Harbor","Docker Hub and GHCR are convenient public services, but they create three real problems as your organization grows or must comply with NIS2 (Art. 21 § 2 f – supply chain security) or GDPR (infrastructure data hosted outside the EU). Harbor addresses each point directly.",{"type":39,"title":40,"items":41},"ul","Key benefits of Harbor",[42,43,44,45,46,47,48],"**Integrated Trivy scanning** — every push triggers a CVE analysis; admission policies block deployment of images exceeding your configured severity threshold.","**Cosign \u002F Notary signing** — chain of trust from CI build to Kubernetes cluster; only images signed by your key pass through.","**Multi-project RBAC** — isolation by team, environment (dev\u002Fstaging\u002Fprod) or client, with Guest, Developer, Maintainer and ProjectAdmin roles.","**Cross-registry replication** — sync to a second VPS, AWS ECR or Docker Hub for CDN distribution.","**Immutable audit logs** — every pull, push, deletion and login is traced with source IP; NIS2 article 21 § 2 d compliance.","**No rate limit** — your CI runners pull as many images as needed without delay or quota.","**Native OIDC** — connect Harbor to ZITADEL or any OpenID Connect provider; zero local accounts to manage.",{"type":35,"title":50,"body":51},"Quantified prerequisites","Harbor runs comfortably on a VPS from the following configuration:\n\n- **CPU**: 4 vCPU (minimum 2 vCPU, but Trivy consumes 1–2 cores during scanning)\n- **RAM**: 8 GB (4 GB minimum; plan 2 GB for PostgreSQL, 1 GB for Redis, 2 GB for Harbor core, 1 GB margin)\n- **Disk**: 100 GB SSD (Docker layers accumulate quickly; allow 40 GB for the registry, 20 GB for Trivy logs, 20 GB for the OS)\n- **OS**: Debian 12 or Ubuntu 22.04 LTS\n- **Docker**: ≥ 24.0, **Docker Compose**: ≥ 2.20\n- **DNS**: a subdomain pointing to your VPS (`registry.example.com`), already resolving\n- **TLS**: valid certificate — Let's Encrypt via Certbot or Traefik ACME",{"type":53,"title":54,"steps":55},"steps","Installing Harbor v2.15 with Docker Compose",[56,59,62,65,68,71,74],{"title":57,"body":58},"Download the offline installer","SSH into your VPS. Fetch the Harbor v2.15.4 offline package (most stable as of 2026-10-13) from GitHub:\n\n```bash\nexport HARBOR_VERSION=v2.15.4\nwget https:\u002F\u002Fgithub.com\u002Fgoharbor\u002Fharbor\u002Freleases\u002Fdownload\u002F${HARBOR_VERSION}\u002Fharbor-offline-installer-${HARBOR_VERSION}.tgz\nwget https:\u002F\u002Fgithub.com\u002Fgoharbor\u002Fharbor\u002Freleases\u002Fdownload\u002F${HARBOR_VERSION}\u002Fharbor-offline-installer-${HARBOR_VERSION}.tgz.asc\n# Verify GPG signature\ngpg --keyserver keyserver.ubuntu.com --recv-keys 7722D168DAEC457894C2988818B80B4B2D318C8A\ngpg --verify harbor-offline-installer-${HARBOR_VERSION}.tgz.asc\ntar xvf harbor-offline-installer-${HARBOR_VERSION}.tgz\ncd harbor\n```",{"title":60,"body":61},"Prepare TLS certificates","Harbor requires TLS. Using Certbot:\n\n```bash\napt install -y certbot\ncertbot certonly --standalone -d registry.example.com\n# Certificates are in \u002Fetc\u002Fletsencrypt\u002Flive\u002Fregistry.example.com\u002F\nmkdir -p \u002Fdata\u002Fcert\ncp \u002Fetc\u002Fletsencrypt\u002Flive\u002Fregistry.example.com\u002Ffullchain.pem \u002Fdata\u002Fcert\u002Fregistry.example.com.crt\ncp \u002Fetc\u002Fletsencrypt\u002Flive\u002Fregistry.example.com\u002Fprivkey.pem  \u002Fdata\u002Fcert\u002Fregistry.example.com.key\n```\n\nIf you use Traefik as a reverse proxy (see the Traefik v3 article), delegate TLS termination to Traefik and configure Harbor in HTTP internally on port 8080.",{"title":63,"body":64},"Configure harbor.yml","Copy the template and adjust the essential values:\n\n```bash\ncp harbor.yml.tmpl harbor.yml\n```\n\nEdit `harbor.yml`:\n\n```yaml\nhostname: registry.example.com\n\nhttps:\n  port: 443\n  certificate: \u002Fdata\u002Fcert\u002Fregistry.example.com.crt\n  private_key: \u002Fdata\u002Fcert\u002Fregistry.example.com.key\n\nharbor_admin_password: ChangeThisPassword!\n\ndatabase:\n  password: ChangeThisToo!\n\ndata_volume: \u002Fdata\n\ntrivy:\n  ignore_unfixed: false\n  skip_update: false\n  offline_scan: false\n  security_check: vuln\n  insecure: false\n```",{"title":66,"body":67},"Run the installer","Harbor offers optional components. Enable Trivy (scanner) and Notary (signatures):\n\n```bash\n.\u002Finstall.sh --with-trivy --with-notary\n```\n\nThe installer generates a `docker-compose.yml` and starts all containers. Verify they are all `healthy`:\n\n```bash\ndocker compose ps\n# Expected STATUS for each service: healthy\n```",{"title":69,"body":70},"First login and password change","Open `https:\u002F\u002Fregistry.example.com` in your browser. Log in with `admin` \u002F the password from `harbor.yml`. Go to **Administration → Users → admin** and immediately change the password.\n\nFrom your workstation or CI runner:\n\n```bash\ndocker login registry.example.com\n# Enter your admin credentials or a service account\n```",{"title":72,"body":73},"Create a project and push your first image","In the Harbor interface, create a project `my-project` (private). Then from your build machine:\n\n```bash\ndocker pull nginx:alpine\ndocker tag nginx:alpine registry.example.com\u002Fmy-project\u002Fnginx:1.27-alpine\ndocker push registry.example.com\u002Fmy-project\u002Fnginx:1.27-alpine\n# In Harbor → my-project → Repositories: the image appears with its Trivy status\n```",{"title":75,"body":76},"Enable automatic TLS renewal","Create a Certbot post-renewal hook to reload Harbor:\n\n```bash\ncat > \u002Fetc\u002Fletsencrypt\u002Frenewal-hooks\u002Fpost\u002Fharbor-reload.sh \u003C\u003C 'EOF'\n#!\u002Fbin\u002Fbash\ncp \u002Fetc\u002Fletsencrypt\u002Flive\u002Fregistry.example.com\u002Ffullchain.pem \u002Fdata\u002Fcert\u002Fregistry.example.com.crt\ncp \u002Fetc\u002Fletsencrypt\u002Flive\u002Fregistry.example.com\u002Fprivkey.pem  \u002Fdata\u002Fcert\u002Fregistry.example.com.key\ncd \u002Fopt\u002Fharbor && docker compose restart proxy\nEOF\nchmod +x \u002Fetc\u002Fletsencrypt\u002Frenewal-hooks\u002Fpost\u002Fharbor-reload.sh\n```",{"type":35,"title":78,"body":79},"Configuration: projects, users and OIDC (ZITADEL)","**Projects and RBAC**\n\nHarbor organizes images by projects. Each project can be public or private, with an independent retention policy. Create one project per team or environment: `frontend`, `backend`, `infra`. Assign members with granular roles:\n\n- **Guest**: pull only\n- **Developer**: push + pull\n- **Maintainer**: manage tags, webhooks and retention policies\n- **ProjectAdmin**: manage members\n\n**OIDC integration with ZITADEL**\n\nIf you have already deployed ZITADEL (see the dedicated article), connect Harbor in a few clicks. In ZITADEL, create a `harbor` application of type *Web*, note the `Client ID` and `Client Secret`. In Harbor → **Administration → Configuration → Authentication**:\n\n- Auth Mode: `OIDC`\n- OIDC Provider Name: `ZITADEL`\n- OIDC Endpoint: `https:\u002F\u002Fsso.example.com`\n- OIDC Client ID \u002F Secret: from ZITADEL\n- OIDC Scope: `openid profile email groups`\n- Check **Verify Certificate**\n\nOn first OIDC login, Harbor automatically creates the user account. Then disable local account creation (**Administration → Configuration → Allow Self-Registration → Off**).",{"type":35,"title":81,"body":82},"Scanning images with Trivy","Harbor embeds Trivy directly into the push pipeline. As soon as an image is pushed, Harbor triggers an asynchronous scan that queries Trivy's CVE database (updated daily). The result appears in the interface: number of vulnerabilities by severity (CRITICAL, HIGH, MEDIUM, LOW, UNKNOWN).\n\n**Enable automatic scan on push**: in Harbor → **Administration → Configuration → Vulnerability** → check *Automatically scan images on push*.\n\n**View a report**: open a repository, click on a tag → *Vulnerabilities* tab. Each CVE lists the affected package, the fixed version and a link to the NVD.\n\n**Trivy version in Harbor v2.15**: Harbor v2.15.4 embeds `trivy-adapter` aligned with Trivy v0.55.x, supporting OS databases (Alpine, Debian, RHEL, Ubuntu), application package managers (npm, pip, cargo, ‎go.mod) and lock files.\n\n**Compliance policies per project**: in Harbor → *Your project* → **Policy** → *Deployment Security* → choose the severity level that blocks pulls. For example, block any pull of an image with at least one unpatched CRITICAL CVE.",{"type":84,"title":85,"body":86},"tip","Security tip: Kubernetes admission policies","Combine Harbor with the **Cosign** admission controller (via Kyverno or Connaisseur) so no unsigned image or one with a CRITICAL CVE can enter your cluster. In your CI pipeline:\n\n```bash\n# Sign the image with Cosign after the build\ncosign sign --key cosign.key registry.example.com\u002Fmy-project\u002Fnginx:1.27-alpine\n# Verify in CI before deployment\ncosign verify --key cosign.pub registry.example.com\u002Fmy-project\u002Fnginx:1.27-alpine\n```\n\nConfigure a Kyverno policy requiring Harbor signing: any pod whose image is not signed by your key is rejected at admission. This closes the most exploited gap in supply chain attacks following CVE-2026-21589.",{"type":35,"title":88,"body":89},"Troubleshooting: 5 real errors and their causes","**1. `Error response from daemon: Get https:\u002F\u002Fregistry.example.com\u002Fv2\u002F: x509: certificate signed by unknown authority`**\nCause: the Let's Encrypt certificate has not yet propagated or you are using a self-signed certificate.\nFix: on each Docker client node, copy `fullchain.pem` to `\u002Fetc\u002Fdocker\u002Fcerts.d\u002Fregistry.example.com\u002Fca.crt` then `systemctl restart docker`.\n\n**2. `harbor-core` restarting in a loop with `Error 1045 (28000): Access denied for user 'root'@'localhost'`**\nCause: the PostgreSQL volume contains data from a previous installation with a different password.\nFix: `docker compose down -v` (⚠️ deletes data), modify the password in `harbor.yml`, rerun `.\u002Fprepare && docker compose up -d`.\n\n**3. Trivy scan stuck on `Fetching vulnerability DB` for 10+ minutes**\nCause: the VPS cannot reach `ghcr.io` (restrictive outbound firewall) to download the Trivy database.\nFix: allow outbound HTTPS to `ghcr.io` and `pkg.dev`, or enable `offline_scan: true` after pre-loading the database with `trivy image --download-db-only`.\n\n**4. `unauthorized: unauthorized to access repository` in CI despite a successful `docker login`**\nCause: the service account does not have the Developer role on the target project.\nFix: in Harbor → *Your project* → **Members** → add the robot or user account with the Developer role or higher.\n\n**5. Harbor interface unreachable after Let's Encrypt certificate renewal**\nCause: the `proxy` container (nginx) continues to serve the old certificate loaded in memory.\nFix: `docker compose restart proxy` from the Harbor installation folder. Ideally, automate this via the post-renewal hook described in step 7.",{"type":91,"title":92,"headers":93,"rows":99},"comparison","Harbor vs Docker Hub vs GHCR vs Gitea Registry",[94,95,96,97,98],"Criterion","Harbor self-hosted","Docker Hub","GHCR","Gitea Registry",[100,106,111,116,122,127,133,138,144],[101,102,103,104,105],"Hosting","Your VPS (EU data)","Docker Inc. servers (USA)","GitHub\u002FMicrosoft servers (USA\u002FEU opt.)","Your VPS",[107,108,109,110,110],"Vulnerability scanning","Integrated Trivy, auto on push","Docker Scout (paid)","None native",[112,113,114,115,110],"Image signing","Cosign + Notary v2","DCT (deprecated)","External Cosign",[117,118,119,120,121],"Granular RBAC","5 roles per project","Limited org roles","GitHub roles (coarse)","Basic Gitea roles",[123,124,125,126,124],"Rate limit (pulls)","None","100\u002F6h (free), 5000\u002Fday (Pro)","Linked to GitHub limits",[128,129,130,131,132],"Native OIDC\u002FSSO","Yes (LDAP, OIDC, SAML)","No (paid enterprise SSO)","GitHub SSO only","Partial OIDC",[134,135,136,137,136],"Complete audit logs","Yes (IP, user, action, tag)","Limited","Partial via GitHub audit",[139,140,141,142,143],"NIS2\u002FGDPR compliance","Full (on-premise data)","Contractual only","Microsoft DPA available","Full (on-premise)",[145,146,147,148,149],"Cost","VPS (~€15–30\u002Fmonth)","Free \u002F Pro ~$5\u002Fmonth","Included in GitHub Free\u002FPro","VPS shared with Gitea","Deploy your Harbor registry on a ServOrbit VPS","A 4 vCPU \u002F 8 GB RAM VPS is enough to run Harbor in production for a team of 10 developers. ServOrbit delivers the VM in under 90 seconds, with root SSH access and automatic snapshots included.","See agency plans","\u002Fsolutions\u002Fagences",[155,170,191],{"id":156,"slug":157,"slugs":158,"title":162,"excerpt":163,"readTime":164,"views":18,"isPinned":19,"publishedAt":165,"updatedAt":21,"category":166,"categories":167,"featuredImage":29,"bgImage":30,"posterImage":169,"relatedSolution":29},434,"traefik-v3-vps-docker-reverse-proxy-https",{"fr":159,"en":157,"ar":160,"es":161},"traefik-v3-vps-reverse-proxy","traefik-v3-على-vps-ريفرس-بروكسي-docker","traefik-v3-vps-reverse-proxy-docker-https","Traefik v3 on VPS: Docker Reverse Proxy with Automatic HTTPS","Deploy Traefik v3 on your VPS: automatic HTTPS with Let's Encrypt, Docker label routing, secured dashboard.",9,"2026-10-12T00:00:00+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":25},[168],{"id":23,"name":24,"slug":25,"color":26,"icon":25},"\u002Fblog\u002Fcovers\u002Ftraefik-v3-vps-reverse-proxy-poster.svg",{"id":171,"slug":172,"slugs":173,"title":177,"excerpt":178,"readTime":179,"views":18,"isPinned":19,"publishedAt":165,"updatedAt":21,"category":180,"categories":185,"featuredImage":29,"bgImage":30,"posterImage":187,"relatedSolution":188},435,"self-hosting-zitadel-vps-multi-tenant-sso",{"fr":174,"en":172,"ar":175,"es":176},"zitadel-sso-self-hosted-vps","استضافة-zitadel-على-vps-sso-متعدد-المستأجرين","zitadel-vps-sso-multi-tenant-sin-keycloak","Self-Hosting ZITADEL on a VPS: Multi-Tenant SSO Without Keycloak","Deploy ZITADEL on your VPS with Docker Compose: cloud-native IAM in Go, native multi-tenancy, OIDC, OAuth2, SAML and Passkeys — without Keycloak's complexity.",14,{"id":17,"name":181,"slug":182,"color":183,"icon":184},"Security & Monitoring","securite-monitoring","bg-rose-500\u002F10 text-rose-400","security",[186],{"id":17,"name":181,"slug":182,"color":183,"icon":184},"\u002Fblog\u002Fcovers\u002Fzitadel-sso-self-hosted-vps-poster.svg",{"categorySlug":189,"appSlug":190},"cybersecurity-bastion","zitadel",{"id":192,"slug":193,"slugs":194,"title":198,"excerpt":199,"readTime":164,"views":18,"isPinned":19,"publishedAt":200,"updatedAt":21,"category":201,"categories":202,"featuredImage":29,"bgImage":30,"posterImage":204,"relatedSolution":29},431,"cve-2026-21589-patch-atlassian-jira-confluence-self-hosted",{"fr":195,"en":193,"ar":196,"es":197},"atlassian-cve-2026-21589-patch-jira-confluence-self-hosted","cve-2026-21589-patch-atlassian-jira-confluence","cve-2026-21589-parche-atlassian-jira-confluence","CVE-2026-21589: patch Atlassian now","CVE-2026-21589 is an unauthenticated path traversal (CVSS 9.3) affecting all Atlassian Data Center products. Learn which versions to patch.","2026-10-11T00:00:00+00:00",{"id":17,"name":181,"slug":182,"color":183,"icon":184},[203],{"id":17,"name":181,"slug":182,"color":183,"icon":184},"\u002Fblog\u002Fcovers\u002Fatlassian-cve-2026-21589-patch-jira-confluence-self-hosted-poster.svg",1791901049395]