# authentik Kubernetes deployment source-of-truth repository for `authentik`. This template is intended for applications deployed with ArgoCD through an `ApplicationSet`. It uses a `base/` plus `overlays/` layout, stores runtime secrets as SOPS-encrypted manifests, and includes a Gitea workflow that checks Kubernetes YAML syntax on every push and pull request. It also ships with a dormant bootstrap `ApplicationSet` that only becomes active after an explicit enable change and a promotion into `main`. ## Layout ```text . ├── .gitea/ │ ├── template │ └── workflows/validate.yaml ├── .github/ │ └── workflows/check-sops-sync.yml ├── .sops.yaml ├── bootstrap/ │ ├── applicationset.yaml │ └── config.yaml ├── manifest/ │ ├── base/ │ │ ├── deployment.yaml │ │ ├── kustomization.yaml │ │ ├── namespace.yaml │ │ └── service.yaml │ ├── components/ │ │ └── example-component/ │ └── overlays/ │ └── production/ │ ├── ingressroute.yaml │ ├── kustomization.yaml │ ├── secret-generator.yaml │ ├── secret.secret.yaml │ └── storage/ │ ├── persistentvolumeclaim.yaml │ └── persistentvolume-nfs.yaml └── scripts/ ├── ci/check-sops-sync ├── git-hooks/pre-commit ├── install-git-hooks └── lib/ ├── sops-path-regexes └── sops-sync-lib ``` ## First edits Update these files before the first deployment: 1. `manifest/base/deployment.yaml` Set the container image, ports, probes, resource requests, and any required environment variables. 2. `manifest/overlays/production/ingressroute.yaml` Set the real hostname, entry point names, and middleware references if used. 3. `manifest/overlays/production/storage/persistentvolumeclaim.yaml` Set the requested size, access mode, and storage class for the environment. 4. `.sops.yaml` Replace the sample age recipient with the public key that ArgoCD should use for decryption. 5. `manifest/overlays/production/secret.secret.yaml` Re-encrypt the placeholder secret values with your own data. ## Bootstrap activation model This template is designed to start on an `init` branch. The intended flow is: 1. Create the repository from this template with `init` as the initial branch. 2. Tailor the manifests, secrets, storage, and ingress on `init`. 3. Keep `bootstrap/config.yaml` set to `enabled: false` while the repository is being prepared. 4. Create `main` by merging `init` only when the repository is ready to be part of the delivery pipeline. 5. Deliberately change `bootstrap/config.yaml` to `enabled: true` on `main` to allow the workflow to apply the bootstrap `ApplicationSet`. The workflow behavior is: - `bootstrap/applicationset.yaml` is ignored by validation while bootstrap is disabled - no cluster apply is attempted unless the workflow runs on `main` - cluster apply also requires the `KUBECONFIG_B64` Gitea secret ## Bootstrap files - `bootstrap/config.yaml` Repository-local activation switch. Default is disabled. - `bootstrap/applicationset.yaml` ArgoCD `ApplicationSet` manifest template for this repository. If the repository is created by copying files with plain Git instead of Gitea's template expansion, you must replace the `${REPO_NAME...}` placeholders in `bootstrap/applicationset.yaml` before enabling bootstrap. ## Storage model Storage is overlay-owned, so each environment can request different backing storage without sharing claims across environments. - `manifest/overlays/production/storage/persistentvolumeclaim.yaml` Default writable application storage. With the default `longhorn` `storageClassName`, Kubernetes dynamically provisions the backing PV when the claim is created. - `manifest/overlays/production/storage/persistentvolume-nfs.yaml` Optional static PV example for NFS-backed or pre-provisioned shared storage. This file is not referenced by default. Add it to the overlay kustomization only when you need a static PV. The base deployment mounts the claim at `/data`. If storage should be shared between multiple applications in different repositories, keep those applications in the same namespace and have a single repository own the PVC resource. The other repositories should reference the same claim name from their deployments instead of creating a duplicate PVC. ## Secret workflow 1. Generate an age key pair. 2. Store the private key where ArgoCD / ksops can read it. 3. Update `.sops.yaml` with the matching public recipient. 4. Re-encrypt `manifest/overlays/production/secret.secret.yaml`. Example: ```bash age-keygen -o age-key.txt sops --encrypt --in-place manifest/overlays/production/secret.secret.yaml ``` ## Repo-managed SOPS sync This repo also supports sync-managed secrets using the naming convention: ```text foo.yaml -> foo.enc.yaml -> foo.enc.yaml.sync-hmac ``` - `foo.yaml` Local plaintext source. Keep it gitignored and untracked. - `foo.enc.yaml` Tracked SOPS-encrypted file. - `foo.enc.yaml.sync-hmac` Tracked HMAC sidecar used to prove the encrypted file still matches the last synced plaintext content. Requirements: - `SOPS_SYNC_HMAC_KEY` must be set locally for hooks and in CI - `sops`, `openssl`, and either `yq` or `python3` with `PyYAML` must be available - CI also needs access to the repo's SOPS decryption mechanism, such as an age key Generate a local HMAC key: ```bash openssl rand -hex 32 ``` Set it for the current shell session: ```bash export SOPS_SYNC_HMAC_KEY='' ``` Persist it in `zsh`: ```bash echo "export SOPS_SYNC_HMAC_KEY=''" >> ~/.zshrc source ~/.zshrc ``` Persist it in `bash`: ```bash echo "export SOPS_SYNC_HMAC_KEY=''" >> ~/.bashrc source ~/.bashrc ``` Verify it is set: ```bash printenv SOPS_SYNC_HMAC_KEY ``` Install the local hook: ```bash scripts/install-git-hooks ``` Local workflow: 1. Keep your plaintext secret local-only, for example `manifest/overlays/production/secret.yaml`. 2. Stage either the plaintext file or its `.enc.yaml` twin. 3. On pre-commit, the hook encrypts the plaintext if needed, updates the `.sync-hmac` sidecar, and stages both tracked files. CI behavior: - CI fails if a tracked `.enc.yaml` file is not valid SOPS content - CI fails if the `.sync-hmac` sidecar is missing - CI fails if decrypted plaintext no longer matches the tracked sidecar HMAC Security tradeoffs: - The sidecar is an HMAC, not a plain checksum, so the plaintext fingerprint is keyed and not directly reusable without `SOPS_SYNC_HMAC_KEY` - Anyone who can decrypt the repo secret files and also access the HMAC key can recompute sidecars, so protect both inputs appropriately - Plaintext local files must remain gitignored and never be committed ## ArgoCD ApplicationSet path Point the generated ArgoCD `Application` at: ```text manifest/overlays/production ``` If your `ApplicationSet` uses repository scanning, this repository is designed to be the single source of truth for one application, with ArgoCD rendering the selected overlay.