Homelab GitOps

Argo CD is the only component that deploys resources from this repository. Application repositories publish immutable images and/or Helm charts, then commit the exact chart version selected by the corresponding Argo Application. CI never receives Kubernetes credentials.

Ownership boundary

  • An application repository owns its image, reusable Kubernetes behavior, probes, services, ingress capability, persistence capability, monitoring capability, tests, and Helm chart.
  • This repository owns only Argo Applications, exact chart selections, cluster values such as domains, addresses, storage classes and sizes, existing PVC names, resource sizing, and sealed credentials.
  • A maintained chart records the upstream or custom application release in appVersion.
  • One independently releasable workload has one chart and one Argo Application. The 13 Artcraft applications are separate charts and Applications but all target the shared artcraft namespace.
  • Upstream charts are referenced directly. GitOps-local dependency wrappers are forbidden.
  • Secrets are either references to existing Secrets or SealedSecret ciphertext. Plaintext never belongs here.

See docs/chart-ownership.md for the complete chart versioning and promotion contract.

Layout

  • bootstrap/ contains AppProjects and Applications.
  • root-application.yaml is the app-of-apps entry point that continuously reconciles bootstrap/; this is what makes CI commits to Application targetRevision fields take effect without kubectl.
  • apps/<name>/values.yaml contains only this cluster's values for that chart.
  • apps/<name>/resources/ is allowed only for genuinely cluster-owned resources, such as a MetalLB address pool or an ImmichFrame SealedSecret.
  • scripts/update-chart-version.sh is the common CI promotion interface.

Version and promotion flow

Maintained chart repositories tag releases with vMAJOR.MINOR.PATCH (or <chart>-vMAJOR.MINOR.PATCH in the shared chart repository). git describe --tags --long is canonical:

  • tag v1.0.0 packages chart 1.0.0;
  • the next commit packages 1.0.0-1-g41627ab;
  • the Git hash is retained to prevent two histories from producing the same immutable package version;
  • dirty trees are rejected.

After publishing succeeds, CI checks out this repository with GITOPS_TOKEN, runs:

./scripts/update-chart-version.sh APPLICATION CHART_VERSION

and commits bootstrap/APPLICATION.yaml. Argo CD observes that Git commit, renders the published chart with the cluster values, and reconciles Kubernetes.

Upstream chart upgrades have no repository under our control, so their exact targetRevision is updated here manually or by a dependency updater. They do not need a fake promotion repository.

Gitea credentials

Prefer owner- or organization-level Actions secrets so every maintained chart repository receives the same narrowly scoped credentials:

  • PACKAGE_USERNAME and PACKAGE_TOKEN: upload-only credentials for the feedc0de Helm package repository;
  • GITOPS_TOKEN: a token with write:repository access to only feedc0de/homelab-gitops;
  • QUAY_USERNAME and QUAY_TOKEN: required only by repositories that publish container images.

The workflow commits as the normal feedc0de Git identity. The token determines authorization; the commit name is not a separate Gitea account. A dedicated promoter account is still preferable if a one-repository permission scope is desired.

Bootstrap and recovery

The expected kubectl context is kubernetes-admin@kubernetes.

./bootstrap/install-argocd.sh
./bootstrap/install.sh

Argo CD manages its own upstream chart after recovery. The repo-server is given cluster-local host aliases for code.brunner.ninja and brunner.ninja, routed through the Traefik ClusterIP, because this cluster cannot hairpin to the public WAN address. The canonical alias configuration is in apps/argocd/values.yaml.

Before adopting a stateful workload, record its PVC UID and volume, render and dry-run the new chart, then verify the same values after synchronization:

kubectl get pvc -A -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,UID:.metadata.uid,VOLUME:.spec.volumeName'
kubectl --namespace argocd get applications \
  -o custom-columns='NAME:.metadata.name,SYNC:.status.sync.status,HEALTH:.status.health.status'

Automatic sync, pruning, and self-healing mean direct live edits are temporary. Change Git and observe Argo return the Application to Synced and Healthy.

Sealed Secrets

Argo CD installs the official Sealed Secrets chart. Public Git stores only SealedSecret ciphertext; the controller's private sealing key must be backed up to encrypted offline storage and never committed.

kubectl --namespace default create secret generic example \
  --from-literal=token='replace-me' \
  --dry-run=client --output=yaml \
  | kubeseal \
      --controller-name sealed-secrets-controller \
      --controller-namespace sealed-secrets \
      --format yaml > example-sealed-secret.yaml
S
Description
No description provided
Readme
620 KiB
Languages
Shell 100%