# Harbor registry

Source: https://docs.quake.ai/resources/iac-templates/harbor-registry
Markdown: https://docs.quake.ai/resources/iac-templates/harbor-registry.md

---

# Harbor registry

This pattern composes Compute, Network, and Block Storage into a self-hosted container registry on infrastructure you control.

## What this template does

Provisions a single instance running [Harbor](https://goharbor.io), an open-source container registry. Harbor stores and serves Docker and OCI images over HTTPS, with projects, role-based access control, and (by default) Trivy vulnerability scanning. Container and Kubernetes pipelines push built images here and pull them at deploy time:

- Compute instance that runs the Harbor stack in Docker, sized to the registry's recommended baseline
- Private network, subnet, router, port, and security group; a floating IP for public access
- A block volume mounted at `/data` (Harbor's `data_volume`), so image layers, the registry database, and the scanner's vulnerability database live on a volume you can grow rather than on the boot disk
- cloud-init installs Docker, generates a self-signed TLS certificate and the admin password, downloads the Harbor installer, and brings the stack up on first boot

No credential ships with this template. The instance generates the admin password and the database password on first boot and writes the admin password to `/root/harbor-credentials` (readable only by root); retrieve it over SSH and rotate it after first login.

## Harbor and the plain registry image

This template runs Harbor, which adds projects, users, replication, and image scanning on top of the registry. For a much lighter target with none of that, the [Distribution `registry`](https://distribution.github.io/distribution/) image runs in a single container with no UI or access control. Harbor is the right pick when you want a registry that people and pipelines share; the plain `registry` image is enough when you only need a private place to push and pull.

## Parameters

| Parameter | Description | Default |
| --- | --- | --- |
| `key_name` | SSH keypair name (must already exist) | No default |
| `flavor_name` | Instance size (8 GiB meets Harbor's recommended baseline) | `s1a.large` |
| `image_name` | Operating system image | `Ubuntu-24.04` |
| `app_name` | Display name prefix for resources | `harbor` |
| `harbor_version` | Harbor release tag to install | `v2.14.4` |
| `domain` | Public domain for the registry; empty uses the floating IP | `""` |
| `volume_size` | Block volume size in GiB, mounted at `/data` | `100` |
| `enable_trivy` | Install the Trivy vulnerability scanner | `true` |
| `external_network` | External network for floating IP allocation | `PublicStatic` |
| `private_cidr` | CIDR for the private subnet | `10.50.0.0/24` |

## Resource baseline

Harbor's documented baseline is 2 vCPU / 4 GiB minimum and 4 vCPU / 8 GiB recommended, because the stack runs a dozen containers: the registry, the core service, the portal, a database, a job service, Redis, and the Trivy scanner. The default `s1a.large` flavor (8 shared vCPU, 8 GiB RAM) meets the recommended memory with headroom for image pushes and scans. To run on a 4 GiB flavor, set `enable_trivy = false` to drop the scanner.

## Ports and access

| Port | Purpose |
| --- | --- |
| 22 | Host SSH for administration and retrieving the admin password |
| 443 | Harbor portal, core API, and docker/OCI push and pull over HTTPS |

Harbor serves HTTPS only; there is no plaintext registry port. On first boot the certificate is self-signed, so a client trusts it by copying `/data/certs/harbor.crt` into its Docker per-registry trust directory before the first `docker login`. For anything exposed to the internet, set `domain`, point its DNS A record at the floating IP, and replace the first-boot certificate with a CA-issued one. The README in the template directory covers both paths.

## How images flow

Harbor groups repositories into projects. The default `library` project is public; create your own projects from the portal for private images. A typical push from a client that trusts the certificate:

```bash
docker login HOST          # username admin, password from /root/harbor-credentials
docker tag myapp:latest HOST/library/myapp:latest
docker push HOST/library/myapp:latest
```

With Trivy enabled, Harbor scans each pushed image for known vulnerabilities and reports them in the portal. You can require a passing scan before an image can be pulled by setting a project's deployment security policy.

## When to use this pattern

Run your own container registry on a VM you operate, so your build and deploy pipelines push and pull images without depending on a third-party registry. It pairs with the [Forgejo git + CI template](/resources/iac-templates/forgejo-git-ci) (build images in CI, push them here) and the [Coolify host](/resources/iac-templates/coolify-host) (pull from here on deploy). A [Kubernetes cluster](/resources/iac-templates/k8s-cluster) pulls images from this registry by referencing `HOST/project/image:tag` in a pod spec, with an image pull secret holding the Harbor credentials.

For a managed build-and-deploy dashboard rather than a registry, use the [Coolify host template](/resources/iac-templates/coolify-host).

## Estimated cost

<PricingCompanion
  components={[
    { kind: "template", slug: "harbor-registry", required: true },
  ]}
/>

## Template source

<TemplateSource slug="harbor-registry" />

<TemplateResourceMap template="harbor-registry" format="opentofu" />

## Customize this pattern

- [Customize a template's image and flavor](/docs/automation/how-to/customize-template-image-flavor)
- [Add a block volume to a template](/docs/automation/how-to/add-volume-to-template)
- [Parameterize a template with a tfvars file](/docs/automation/how-to/parameterize-template-tfvars)

## See also

- [Forgejo git + CI template](/resources/iac-templates/forgejo-git-ci)
- [Kubernetes cluster template](/resources/iac-templates/k8s-cluster)
