Important
This operator does not install Harbor in “real” clusters.
It assumes Harbor is already running and introduces a HarborConnection CRD
or ClusterHarborConnection CRD that stores connection details for that instance.
All other CRDs reference one of those connection objects.
harbor-operator is a Kubernetes operator that lets you manage Harbor resources — registries, projects, users, and members — as Kubernetes Custom Resources (CRs).
Instead of clicking around in the Harbor UI, you describe your desired state in YAML. The operator then reconciles that state with your Harbor instance via its API.
Note
You may see kubectl warnings like unrecognized format "int64" when applying CRDs.
These are client-side validation warnings from kubectl and are safe to ignore.
-
HarborConnection
Connection details for an existing Harbor instance (base URL, optional credentials). Namespaced and intended for tenant-local use. -
ClusterHarborConnection Cluster-scoped connection details for a shared Harbor instance. Use this when multiple namespaces should reference the same Harbor endpoint and credentials.
-
Registry
Represents a Harbor “target registry” (e.g. GHCR) and its configuration. -
Project
Represents a Harbor project, including metadata (public/private, auto-scan, etc.). -
User
Represents a Harbor user and (optionally) its lifecycle. -
Member
Represents membership of a user or group in a Harbor project with a given role. -
Robot
Represents a Harbor robot account (system or project level). -
Configuration
Represents Harbor system configuration (OIDC, robot defaults, etc.). -
ReplicationPolicy
Represents Harbor replication policies between registries. -
WebhookPolicy
Represents project-level Harbor webhook policies. -
ImmutableTagRule
Represents project-level immutable tag rules. -
Label
Represents Harbor labels (global or project-scoped). -
UserGroup
Represents Harbor user groups (LDAP/HTTP/OIDC). -
ScannerRegistration
Represents Harbor scanner registrations. -
ScanAllSchedule
Represents Harbor scan-all scheduling configuration. -
Quota
Represents Harbor project quota configuration.
- Go
- Docker (or compatible container runtime)
- kubectl
- A Kubernetes cluster (Kind is fine for dev)
This repo ships with a Kind-based dev environment that:
- Creates a Kind cluster
- Installs Traefik
- Installs Harbor via the official Helm chart
- Builds and deploys harbor-operator into that cluster
This is just for development convenience. In a real environment, you bring your own Harbor.
Add this line to /etc/hosts (or your platform’s hosts file):
127.0.0.1 core.harbor.domainmake kind-upThis will:
- Create a Kind cluster using
hack/kind-configuration.yaml - Install Traefik (NodePorts 30080/30443 by default)
- Install Harbor via Helm
- Build a local
harbor-operator:localimage, load it into Kind, and deploy it
make kind-up uses Kind's default CNI and has the fastest startup time.
If you want Cilium to be the CNI (so Hubble can observe pod traffic), create the cluster with Cilium from the start:
make kind-up-ciliummake kind-up-cilium uses Cilium and Hubble. Startup is slower than make kind-up.
Apply or remove sample CRs (in config/samples):
# Apply sample HarborConnection, Registry, Project, etc.
make apply-samples
# Delete all Harbor CRs (connection objects last) in all namespaces
make delete-crs
delete-crsremoves all custom resources in theharbor.harbor-operator.ioAPI group, not just the manifests inconfig/samples/.
For iterative development:
make kind-refreshThis will:
- Rebuild the operator image
- Load it into the existing Kind cluster
- Regenerate code and manifests
- Sync CRDs and RBAC into the Helm chart
- Apply the latest CRDs to the cluster
- Redeploy the operator with Helm
The controller runs inside the Kind cluster, so code changes take effect after rebuilding and redeploying the operator image.
Use make kind-refresh after changing:
- controller logic
- Harbor client code
- CRD Go types
- generated CRDs
- RBAC
- Helm chart wiring for the operator
If you already have a Kind cluster and supporting stack running, you can deploy the operator onto that current cluster with:
make kind-deploykind-deploy and kind-refresh run the same deployment steps with different intent:
kind-deployis the base "deploy onto this Kind cluster" targetkind-refreshis the normal iterative developer workflow
If you want to wipe operator-managed CRs, remove the operator, remove CRDs, and then install everything again from scratch:
make kind-redeployThis will:
- Delete all Harbor CRs managed by the operator (connection objects last)
- Remove the operator deployment and CRDs
- Rebuild the image
- Load it into Kind
- Reinstall CRDs and redeploy the operator
Typical uses:
- you intentionally want a clean slate
- a CRD change is incompatible with existing CRs
- you want to re-test first-install behavior
The high-level workflow above is usually enough, but these targets are useful when working on packaging and deployment details:
make prepare-deploy
make sync-chart
make apply-crds
make kind-load-imageprepare-deployregenerates code/manifests, syncs chart assets, and applies CRDssync-chartcopies generated CRDs and RBAC into the Helm chartapply-crdsapplies the current chart CRDs to the clusterkind-load-imageonly loads the already-built local image into Kind
To delete only the Kind cluster:
make kind-downThe repository ships two documentation layers:
- hand-written operator guides under
docs/crds/ - generated schema reference under
docs/reference/api.md
The documentation site is built with MkDocs Material and deployed from main to GitHub Pages.
In the repository settings, GitHub Pages should be configured to deploy from GitHub Actions.
Generate the API reference with:
make generate-docsBuild the MkDocs site locally with:
make docs-buildServe it locally with:
make docs-serveWe publish an OCI Helm chart to GHCR.
helm registry login ghcr.io
helm install harbor-operator oci://ghcr.io/rkthtrifork/charts/harbor-operator --version <chart-version>To render locally:
helm template harbor-operator oci://ghcr.io/rkthtrifork/charts/harbor-operator --version <chart-version>Values are documented in charts/harbor-operator/values.yaml and validated by charts/harbor-operator/values.schema.json.
Create either a HarborConnection or a ClusterHarborConnection, then point your Harbor CRs at it with spec.harborConnectionRef.
- Updates to
HarborConnectionandClusterHarborConnectiontrigger reconciliation of Harbor-backed CRs that reference them. spec.deletionPolicydefaults toDelete. UseOrphanwhen you want Kubernetes deletion to proceed even if Harbor cleanup cannot be completed.Configuration,GCSchedule,PurgeAuditSchedule, andScanAllScheduleare singleton-style Harbor APIs. Only one CR may manage each of those per Harbor instance. If multiple CRs target the same Harbor instance, the oldest CR remains the owner and later CRs report a conflict.
The operator supports Prometheus metrics via controller-runtime. Metrics are disabled by default.
To enable via Helm:
helm upgrade --install harbor-operator oci://ghcr.io/rkthtrifork/charts/harbor-operator \
--version <chart-version> \
--set metrics.enabled=trueIf you want to remove Harbor-managed resources, CRDs, and the operator:
# Remove Harbor CRs (connection objects last)
make delete-crs
# Remove the operator
helm uninstall harbor-operator -n harbor-operator-system
# Remove CRDs for the harbor.harbor-operator.io API group
make uninstallIn a Kind dev cluster, a full reset is just:
make kind-reset
# or
make kind-downIf you are new to the repo, the shortest path to a working local environment is:
make kind-upor, if you want Cilium/Hubble:
make kind-up-ciliumThen iterate with:
make kind-refreshAnd if you need a full reset:
make kind-redeploy-
Format and vet:
make fmt vet
-
Run tests:
make test -
Run linters:
make lint
Open a PR with a clear description of what you changed and why.