5.0 KiB
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
artcraftnamespace. - Upstream charts are referenced directly. GitOps-local dependency wrappers are forbidden.
- Secrets are either references to existing Secrets or
SealedSecretciphertext. 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.yamlis the app-of-apps entry point that continuously reconcilesbootstrap/; this is what makes CI commits to ApplicationtargetRevisionfields take effect without kubectl.apps/<name>/values.yamlcontains 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 ImmichFrameSealedSecret.scripts/update-chart-version.shis 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.0packages chart1.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_USERNAMEandPACKAGE_TOKEN: upload-only credentials for thefeedc0deHelm package repository;GITOPS_TOKEN: a token withwrite:repositoryaccess to onlyfeedc0de/homelab-gitops;QUAY_USERNAMEandQUAY_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