- ExternalSecret now pulls only HARBOR_ADMIN_PASSWORD from 1Password - Removed database, redis, core, jobservice, registry secret references - Harbor Helm chart auto-generates all internal secrets (standard pattern) - Reduces complexity and aligns with Harbor best practices This change removes dependency on 5 1Password fields that should be deleted: - database-password - redis-password - core-secret - jobservice-secret - registry-password Only harbor-admin-password field needed in 1Password item 'the-seas'
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
- Primary URL: https://the-seas.local.mk-labs.cloud
- Alternate URL: https://harbor.local.mk-labs.cloud (for intuitive
docker login harbor...) - Namespace: the-seas
- 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 Names
- Primary: the-seas.local.mk-labs.cloud
- Alternate: harbor.local.mk-labs.cloud
Both DNS names resolve to the Cilium Gateway IP (10.1.71.90) via external-dns.
TLS Certificates
- Issued by Let's Encrypt (letsencrypt-prod ClusterIssuer)
- Managed by cert-manager
- Separate certificates for each hostname
- Stored as Kubernetes secrets:
the-seas-tls,harbor-tls
Routing
- Gateway: fastpass-gateway (namespace: gateway)
- Protocol: HTTPS (port 443)
- Backend: ClusterIP service
the-season port 80 - TLS termination at Gateway 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/