DNS was pointing to Gateway (10.1.71.90) instead of NGINX Ingress (10.1.71.80) after we removed the HTTPRoute manifests. This caused traffic to hit the Gateway's wildcard cert and get 'no healthy upstream' errors. Added external-dns annotations to Ingress to direct DNS to correct IP: - external-dns.alpha.kubernetes.io/hostname: the-seas.local.mk-labs.cloud - external-dns.alpha.kubernetes.io/target: 10.1.71.80 This will update Technitium DNS to point to NGINX Ingress Controller.
The Seas - Harbor Container Registry
EPCOT Theme: The Seas with Nemo & Friends
Service: Harbor Container Registry
Wave: 7
Overview
Harbor is an open-source container registry that provides trust, compliance, performance, and interoperability. It serves as the central image repository for all custom-built containers in the mk-labs homelab.
Service Details
- URL: https://the-seas.local.mk-labs.cloud
- Namespace: harbor
- Sync Wave: 7 (deploys after Gateway API, cert-manager, ExternalSecrets)
Architecture
Deployment Model
- Database: Embedded PostgreSQL (internal to Harbor)
- Cache: Embedded Redis (internal to Harbor)
- HA Mode: Single instance (no HA for homelab simplicity)
- Storage: NFS via nfs-emporium StorageClass
Versions
- Helm Chart: 1.19.1
- Harbor: v2.15.1
- All component images: Pinned to 2.15.1 tag
Components
Harbor consists of several microservices:
- Portal - Web UI (1 replica)
- Core - Main API server (1 replica)
- Registry - OCI/Docker registry backend (1 replica)
- Job Service - Async job processor (1 replica)
- Database - PostgreSQL for metadata (embedded)
- Redis - Cache and job queue (embedded)
- Trivy - Vulnerability scanner (enabled)
- Exporter - Prometheus metrics exporter (enabled)
- NGINX - Reverse proxy frontend (1 replica)
Storage Configuration
| Component | StorageClass | Size | Purpose |
|---|---|---|---|
| Registry | nfs-emporium | 100Gi | Container images and Helm charts |
| Database | nfs-emporium | 10Gi | Metadata and configuration |
| Redis | nfs-emporium | 5Gi | Cache and job queue persistence |
| JobService | nfs-emporium | 5Gi | Job logs |
| Trivy | nfs-emporium | 10Gi | Vulnerability database |
Total: ~130Gi storage provisioned
Resource Allocation
Approximate resource requests/limits per component:
- Core: 512Mi-1Gi RAM, 0.5-1 CPU
- Registry: 1-2Gi RAM, 0.5-1 CPU
- Database: 1-2Gi RAM, 0.5-2 CPU
- Redis: 256-512Mi RAM, 0.25-0.5 CPU
- Portal: 256-512Mi RAM, 0.25-0.5 CPU
- JobService: 512Mi-1Gi RAM, 0.5-1 CPU
- Trivy: 512Mi-1Gi RAM, 0.5-1 CPU
- Exporter: 256-512Mi RAM, 0.25-0.5 CPU
- NGINX: 256-512Mi RAM, 0.25-0.5 CPU
Total: ~6-10Gi RAM, ~4-8 CPU cores across all pods
Secrets Management
All credentials are stored in 1Password and synchronized to Kubernetes via ExternalSecrets Operator.
1Password Item: the-seas (vault: mk-labs)
Required Secrets:
harbor-admin-password- Web UI admin user passworddatabase-password- PostgreSQL passwordredis-password- Redis passwordcore-secret- Harbor core component secretjobservice-secret- Job service authentication secretregistry-password- Registry backend password
Kubernetes Secret: the-seas-credentials (namespace: the-seas)
Network & TLS
DNS Name
- Hostname: the-seas.local.mk-labs.cloud
DNS resolves to the NGINX Ingress Controller IP (10.1.71.80).
TLS Certificate
- Issued by Let's Encrypt (letsencrypt-staging ClusterIssuer)
- Managed by cert-manager (automatic via Ingress annotation)
- Certificate stored as Kubernetes secret:
harbor-tls
Routing
- Ingress Controller: NGINX (nginx-ingress)
- Protocol: HTTPS (port 443)
- Backend: Harbor NGINX service on port 80
- TLS termination at Ingress level
Access & Authentication
Web UI
- Navigate to https://the-seas.local.mk-labs.cloud or https://harbor.local.mk-labs.cloud
- Username:
admin - Password: From 1Password item
the-seas, fieldharbor-admin-password
Docker CLI
# Login to registry
docker login the-seas.local.mk-labs.cloud
# or
docker login harbor.local.mk-labs.cloud
# Username: admin
# Password: <from 1Password>
Robot Accounts
For CI/CD automation, robot accounts are created post-deployment:
-
tekton-builder - For Tekton CI/CD image pushes
- Permissions: Push artifacts, Create tags
- Token stored in 1Password:
the-seas-tekton-robot
-
argocd-puller - For ArgoCD image metadata reads
- Permissions: Pull artifacts, Read metadata (read-only)
- Token stored in 1Password:
the-seas-argocd-robot
Initial Project Structure
Harbor organizes images into projects. Initial projects:
- library - General-purpose images (default)
- platform - Infrastructure/platform images (Tekton, etc.)
- applications - Application images (Firecrawl, etc.)
All projects are private by default.
Deployment
Prerequisites
- ArgoCD deployed and managing cluster
- Gateway API (wave 4) deployed
- cert-manager (wave 3) deployed
- ExternalSecrets Operator (wave 1) deployed
- 1Password Connect (wave 2) deployed
- 1Password item
the-seascreated with required secrets
Apply
# From repository root
kubectl apply -f cluster/platform/the-seas/application.yaml
# ArgoCD will sync automatically
Verify
# Check ArgoCD application status
kubectl get application -n argocd the-seas
# Check all pods are running
kubectl get pods -n the-seas
# Check PVCs are bound
kubectl get pvc -n the-seas
# Check ExternalSecret synced
kubectl get externalsecret -n the-seas the-seas-credentials
# Check certificates issued
kubectl get certificate -n the-seas
Testing
Basic Functionality
# 1. Web UI access
curl -k https://the-seas.local.mk-labs.cloud
# Should return Harbor web UI HTML
# 2. Docker login
docker login the-seas.local.mk-labs.cloud
# Should prompt for credentials and succeed
# 3. Push test image
docker pull alpine:latest
docker tag alpine:latest the-seas.local.mk-labs.cloud/library/alpine:test
docker push the-seas.local.mk-labs.cloud/library/alpine:test
# 4. Pull test image
docker rmi the-seas.local.mk-labs.cloud/library/alpine:test
docker pull the-seas.local.mk-labs.cloud/library/alpine:test
# 5. Verify in web UI
# Navigate to Projects → library → Repositories
# Should see alpine image with test tag
Certificate Validation
# Check TLS certificate
echo | openssl s_client -connect the-seas.local.mk-labs.cloud:443 2>/dev/null | openssl x509 -noout -text | grep -A 2 "Subject:"
# Verify certificate is valid
echo | openssl s_client -connect the-seas.local.mk-labs.cloud:443 2>/dev/null | grep "Verify return code"
# Should show: Verify return code: 0 (ok)
Monitoring
Harbor exports Prometheus metrics on port 8001 for several components:
- Core:
/metrics - Registry:
/metrics - JobService:
/metrics - Exporter:
/metrics
Metrics are enabled in values.yaml and can be scraped by Prometheus.
Troubleshooting
Pods CrashLoopBackOff
Check pod logs:
kubectl logs -n the-seas <pod-name>
Common issues:
- Database init failures: Check database password in ExternalSecret
- Redis connection errors: Check redis password in ExternalSecret
- Secret not found: Verify ExternalSecret synced successfully
Cannot Access Web UI
- Check Gateway and HTTPRoute:
kubectl get gateway -n gateway fastpass-gateway
kubectl get httproute -n the-seas
- Check DNS resolution:
nslookup the-seas.local.mk-labs.cloud
# Should resolve to 10.1.71.90
- Check certificate:
kubectl get certificate -n the-seas the-seas-tls
# Should show Ready=True
Docker Login Fails
- Check DNS resolution
- Check certificate is trusted
- Verify admin password from 1Password is correct
- Check Harbor core service is running:
kubectl get svc -n the-seas the-seas
kubectl get pods -n the-seas -l component=core
Image Push/Pull Fails
- Check registry pod:
kubectl get pods -n the-seas -l component=registry
kubectl logs -n the-seas -l component=registry
- Check storage PVC is bound:
kubectl get pvc -n the-seas
- Check project permissions in Harbor UI
Maintenance
Backup
Harbor data is persisted on NFS volumes. Ensure NFS storage is backed up regularly.
Critical data:
/storageon registry PVC - All container images- Database PVC - All metadata, users, projects, policies
Updates
Harbor is managed by ArgoCD. To update:
- Update
targetRevisioninapplication.yamlto new chart version - Review changelog: https://github.com/goharbor/harbor/releases
- Update image tags in
values.yamlif needed - Commit and push to Git
- ArgoCD will automatically sync
Scaling
Current deployment is single-instance. To scale:
-
Update replica counts in
values.yaml:portal.replicascore.replicasregistry.replicasjobservice.replicas
-
Consider external PostgreSQL and Redis for HA
References
- Harbor Documentation: https://goharbor.io/docs/
- Harbor Helm Chart: https://github.com/goharbor/harbor-helm
- Repository Path:
/home/hermes/git/homelab/cluster/platform/the-seas/ - ArgoCD Application: https://argocd.mk-labs.cloud/applications/the-seas
- 1Password Vault: mk-labs
- 1Password Item: the-seas
Next Steps
- Phase 2: Integrate with Tekton (innoventions) for CI/CD
- Phase 3: Configure vulnerability scanning policies
- Phase 4: Implement image signing with Cosign (Notary v2)
- Phase 5: Set up replication to backup registry (if needed)
Support
For issues or questions:
- Check troubleshooting section above
- Review Harbor logs:
kubectl logs -n the-seas <pod-name> - Consult Harbor documentation: https://goharbor.io/docs/
- Escalate to Ryan via
/mnt/mk-labs-pka/ryan-inbox/