Skip to content

Harbor

Harbor is the platform’s private container image registry. It hosts images built by the Forgejo Actions runner (notably the data-workflows/scrapy and data-workflows/dbt images), backs in-cluster imagePullSecrets, and is reachable only over the VPN.

FieldValue
Flux pathflux-clusters/stefanzhelev/apps/harbor
Base pathflux-apps/harbor
Namespaceharbor
Sync wave6
Depends oncloudnative-pg-config, external-secrets-config, authentik-app-harbor
  • harbor-config — HelmRelease for the upstream harbor chart >= 1.13.0, configured with the shared CNPG database and admin password from External Secrets
  • harbor-database — Terraform CR that provisions Harbor’s PostgreSQL databases on the shared CNPG cluster
  • harbor-vault-secrets — Terraform CR generating the admin + secretKey at secret/harbor
  • harbor-external-secrets — ExternalSecret rendering admin password, secretKey, and DB credentials into the namespace
  • harbor-oidc-bootstrap — one-shot Job that wires Harbor’s OIDC settings against Authentik via the Harbor admin API (Harbor doesn’t accept OIDC config through Helm values)
Hostnameharbor.internal.stefanzhelev.com
Reachable fromNetBird VPN only (internalsecure Traefik entrypoint)
TLScert-manager, letsencrypt-prod (DNS-01)
SSOOIDC via Authentik — also accepts the bootstrap admin user/password
External URLhttps://harbor.internal.stefanzhelev.com

The registry is not exposed on the public Hetzner LB — traffic only arrives via the internalsecure entrypoint that’s routed exclusively from the VPN-internal CIDR.

  • CloudNative-PG: Harbor’s core, notary, and related schemas live on the shared cluster
  • Vault + Tofu Controller: admin + secretKey originate in Vault, generated by harbor-vault-secrets
  • Authentik: OIDC SSO, bootstrapped post-install by harbor-oidc-bootstrap
  • Forgejo Actions runner: push robot accounts (created by data-workflows-docker-registry) push freshly-built images here; in-cluster pods pull them via Kubernetes imagePullSecrets
Terminal window
# Connect to the NetBird VPN first, then:
docker login harbor.internal.stefanzhelev.com
# Username + password come from secret/harbor in Vault, or use SSO via the web UI to mint a CLI secret.
Terminal window
docker tag myapp:latest harbor.internal.stefanzhelev.com/library/myapp:latest
docker push harbor.internal.stefanzhelev.com/library/myapp:latest
Terminal window
docker pull harbor.internal.stefanzhelev.com/library/myapp:latest
spec:
containers:
- name: app
image: harbor.internal.stefanzhelev.com/library/myapp:latest
imagePullSecrets:
- name: harbor-pull-secret

The data-workflows namespace already has a docker-registry Secret rendered by data-workflows-docker-registry’s pull robot, so the existing scrapy/dbt DAGs pull from Harbor without any per-DAG setup.