Skip to content

YourBOX Kubernetes GitOps

ADR-0010 sets the homelab Kubernetes cluster as the production target for YourBOX, and ADR-0011 sets Argo CD as the GitOps engine for every workload in the workspace. This page documents the Helm chart layout, the Argo CD wiring under homelab-platform, the secret projection through External Secrets, the platform dependencies and the security controls.

The chart lives in the YourBOX repository. The Argo CD wiring lives in the homelab platform repository. The split keeps the application’s release lifecycle inside the application repository and the platform reconciliation inside the platform repository.

yourbox/
└── k8s/
└── charts/yourbox/
├── Chart.yaml
├── values.yaml
└── templates/
├── deployment.yaml
├── service.yaml
├── httproute.yaml
├── serviceaccount.yaml
├── networkpolicy.yaml
├── externalsecret.yaml
├── servicemonitor.yaml
├── poddisruptionbudget.yaml
├── horizontalpodautoscaler.yaml
└── worker-deployment.yaml
homelab-platform/
└── kubernetes/
└── apps/yourbox/
├── application.yaml # Argo CD Application
├── registry-secret-sealed.yaml
├── postgres-cluster.yaml
├── meilisearch.yaml
├── minio-tenant.yaml
└── kustomization.yaml

The platform repository keeps the source of truth for what is deployed. The Argo CD Application resource references the Helm chart from the YourBOX repository at a tagged version and pins the image digest through the values override.

---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: yourbox
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: applications
source:
repoURL: https://gitlab.yourbox.site/yourbox/yourbox.git
targetRevision: v0.4.2
path: k8s/charts/yourbox
helm:
valueFiles:
- values.yaml
parameters:
- name: image.digest
value: "sha256:<digest>"
- name: image.tag
value: "0.4.2"
destination:
server: https://kubernetes.default.svc
namespace: yourbox
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=false
- ServerSideApply=true
- ApplyOutOfSyncOnly=true
retry:
limit: 5
backoff:
duration: 10s
factor: 2
maxDuration: 3m
revisionHistoryLimit: 10

The Argo CD app of apps pattern picks up the Application resource from the homelab-platform/kubernetes/apps/yourbox/ directory through a Kustomization rendered by a parent Application. Adding YourBOX to the production set is one commit on the platform repository.

The CI deploy stage opens a Merge Request against the YourBOX Application to bump the image.digest parameter. A reviewer merges and Argo CD reconciles within 5 minutes. A rollback is a revert of that MR.

The chart values mirror the env contract from ADR-0008. The image is digest pinned at the values level per ADR-0009.

image:
repository: registry.yourbox.site/yourbox
tag: "0.4.2"
digest: "sha256:<digest>"
pullPolicy: IfNotPresent
application:
replicas: 2
resources:
limits: { cpu: "1", memory: "512Mi" }
requests: { cpu: "200m", memory: "256Mi" }
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile: { type: RuntimeDefault }
containerSecurityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
worker:
replicas: 1
resources:
limits: { cpu: "2", memory: "1Gi" }
requests: { cpu: "500m", memory: "512Mi" }
env:
APP_ENV: prod
APP_DEBUG: "0"
externalSecrets:
enabled: true
refreshInterval: 5m
store: openbao-prod
remoteRef: yourbox/prod
httpRoute:
enabled: true
parentRefs:
- name: traefik
namespace: networking
hostnames: ["yourbox.site"]
tls:
enabled: true
cache:
enabled: true
externalDsnSecretRef:
name: yourbox-env
key: CACHE_DSN
postgres:
externalDsnSecretRef:
name: yourbox-env
key: DB_DSN
meilisearch:
externalUrlSecretRef:
name: yourbox-env
key: MEILISEARCH_URL
externalKeySecretRef:
name: yourbox-env
key: MEILISEARCH_KEY
storage:
externalDsnSecretRef:
name: yourbox-env
key: STORAGE_DSN
oidc:
issuerSecretRef:
name: yourbox-env
key: OIDC_ISSUER
clientIdSecretRef:
name: yourbox-env
key: OIDC_CLIENT_ID
clientSecretSecretRef:
name: yourbox-env
key: OIDC_CLIENT_SECRET

The image reference rendered in the deployment template combines the tag and the digest:

image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}@{{ .Values.image.digest }}"

The ExternalSecret resource (rendered by the chart) reads from OpenBao and writes a Kubernetes Secret named yourbox-env. The Deployment projects the secret as environment variables via envFrom.secretRef.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: yourbox-env
namespace: yourbox
spec:
refreshInterval: 5m
secretStoreRef:
name: openbao-prod
kind: ClusterSecretStore
target:
name: yourbox-env
template:
type: Opaque
dataFrom:
- extract:
key: yourbox/prod

The ClusterSecretStore openbao-prod is defined in the platform repository and authenticates to OpenBao through the Kubernetes auth method scoped to the yourbox namespace and the yourbox ServiceAccount.

The GitLab registry secret is delivered through a SealedSecret. SealedSecrets remain the bootstrap path for credentials that must exist before External Secrets can authenticate the application pod.

The chart consumes platform services. Each is owned by the homelab platform repository, not by the YourBOX repository.

Dependency Operator or chart Source
PostgreSQL 16 CloudNativePG kubernetes/apps/yourbox/postgres-cluster.yaml
Meilisearch Meilisearch Helm chart kubernetes/apps/yourbox/meilisearch.yaml
MinIO MinIO Tenant CRD kubernetes/apps/yourbox/minio-tenant.yaml
Redis cache Bitnami Redis chart kubernetes/apps/yourbox/redis.yaml
Messaging bus (see ADR-0011) platform decision pending
Authentik Authentik Helm chart kubernetes/platform/authentik/
OpenBao + ESO platform admin plane kubernetes/platform/external-secrets/
Sigstore policy policy-controller kubernetes/platform/security/

The platform repository owns the lifecycle of these dependencies (backups, upgrades, alerts). The YourBOX chart only declares the consumption contracts through Secret refs and Service refs. The Redis cache and the messaging bus are introduced per the platform engineering decisions in ADR-0011.

The default deny applies. Egress is allowed to:

  • The cluster DNS (kube-dns on port 53/UDP).
  • The PostgreSQL cluster (the yourbox-postgres-rw service on port 5432).
  • The Meilisearch service (port 7700).
  • The MinIO tenant service (port 9000).
  • The Redis cache service (port 6379).
  • The Authentik service (port 9000) for the OIDC token exchange.
  • The Mailer egress to the SMTP provider declared in SMTP_DSN.
  • The messaging bus service (port to be defined per ADR-0011).

Ingress is allowed from:

  • The Traefik Gateway in the networking namespace on port 8080.
  • The Prometheus operator’s ServiceMonitor scrape (port 8080 path /metrics).
  • The cosign policy controller probe.

The NetworkPolicy template lives at yourbox/k8s/charts/yourbox/templates/networkpolicy.yaml and is rendered from the values.

Kyverno enforces three policies on the YourBOX namespace, defined in homelab-platform/kubernetes/platform/security/:

  • Every container image reference must carry a digest. A pod with a non digest image is rejected.
  • Every container must run as a non root UID and must drop all Linux capabilities. The Helm chart’s pod template satisfies both.
  • Every container image must come from registry.yourbox.site/* or an allowlisted public registry that matches .plumber.yaml.

The Sigstore policy controller verifies that the image is signed by the cosign key whose public part is committed in homelab-platform/kubernetes/platform/security/cosign-pub.yaml. An unsigned image is rejected by admission.

A single ApplicationSet generates one Argo CD Application per environment (staging, production) by reading a generator file in the platform repository. The generator captures per environment overrides (cluster, namespace, image digest, replica count) and applies them through Helm value overrides.

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: yourbox-environments
namespace: argocd
spec:
generators:
- list:
elements:
- env: staging
cluster: in-cluster
namespace: yourbox-staging
digest: sha256:<staging-digest>
replicas: 1
- env: production
cluster: in-cluster
namespace: yourbox
digest: sha256:<prod-digest>
replicas: 2
template:
metadata:
name: yourbox-{{env}}
spec:
project: applications
source:
repoURL: https://gitlab.yourbox.site/yourbox/yourbox.git
targetRevision: v0.4.2
path: k8s/charts/yourbox
helm:
parameters:
- name: image.digest
value: "{{digest}}"
- name: application.replicas
value: "{{replicas}}"
destination:
server: https://kubernetes.default.svc
namespace: "{{namespace}}"
syncPolicy:
automated: { prune: true, selfHeal: true }

The Helm chart renders a ServiceMonitor consumed by the cluster’s Prometheus operator. The YourBOX pod exposes a /metrics endpoint guarded by an internal network policy.

The pods are picked up by Promtail in the logging namespace and indexed in Loki. Grafana dashboards live at yourbox/k8s/charts/yourbox/dashboards/ and are deployed as ConfigMaps with the grafana_dashboard label.

The Observability page details the dashboard panels, alert rules and log schema.

The promotion path matches the staged deployment from ADR-0010. A commit to main produces a digest pinned image, triggers the VPS deploy, then Azure, then opens the MR against homelab-platform/kubernetes/apps/yourbox/ to update the digest. Argo CD reconciles within 5 minutes.

A rollback is a revert of the MR that bumped the digest. Argo CD reverts within 5 minutes and the pod template restarts onto the previous digest.