# 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](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//values.yaml` contains only this cluster's values for that chart. - `apps//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 `-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: ```sh ./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`. ```sh ./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: ```sh 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. ```sh 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 ```