- Shell 55.6%
- HCL 23.2%
- Just 21.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| docs | ||
| just | ||
| proto-plugins | ||
| scripts | ||
| services | ||
| .gitignore | ||
| .prototools | ||
| .sops.yaml | ||
| justfile | ||
| kustomization.yaml | ||
| main.tf | ||
| README.md | ||
homelab
Personal homelab, managed as code: a single-node Scaleway Kapsule cluster
(Terraform), Traefik for ingress/TLS, lldap + Authelia for identity, and
Forgejo as the code forge. Also
meant to work as a template/teaching resource — see docs/ for
the full documentation (start with
docs/tutorials/01-build-the-homelab.md
if you're new here).
Commands
Common operations are just recipes — run just on its own to list them:
just install # install the pinned toolchain (proto)
just deploy # apply secrets (in order) then the kustomization
just k8s diff # what deploy would change, secrets included
just k8s show-status # pods, cronjobs, ingresses
just check # link check, shell syntax, kustomize build, terraform validate
just reconcile # reconcile lldap accounts from services/lldap-accounts/
just backup # run a backup now instead of waiting for 03:00
just restic list-snapshots # list restic snapshots
just restic restore # interactive restore — destroys live data
just tf plan # terraform plan
just tf apply # terraform apply
just deploy exists because the ordering matters: identity-shared must be
applied before the workloads that reference its keys, and Kustomize can't
decrypt the SOPS secrets at all. Recipes are organized into per-domain modules
(tf, k8s, restic, docs), with orchestrators (deploy, check,
reconcile, backup, install) at the root. See
ADR 13 for the task-runner choice and
ADR 18 for the
module structure.
Anything not covered by a recipe is a recipe in
docs/how-to/ — those are the multi-step procedures.
Workflow
- Everything deployable lives under
services/*.yaml, listed inkustomization.yaml. TwoconfigMapGeneratorentries pull the reconciler and backup scripts in from real files; no overlays or patches. - No CI/CD — changes are edited locally and applied by hand (
just deploy/just tf apply). There's no server-side reconciliation: if you edit a file and don't run the apply command, nothing happens. - Run
just checkandjust k8s diffbefore applying anything non-trivial. For anything that restarts a shared component (Traefik, in particular — a restart briefly affects every service behind it), read the diff first. - Secrets are SOPS+age-encrypted, in
services/*.secrets.yamlfiles kept deliberately out ofkustomization.yaml(Kustomize can't decrypt them) — apply them withsops -d <file> | kubectl apply -f -, a separate step fromkubectl apply -k .. Never put a sensitive value in a plainConfigMap, and never put aSecretobject outside a*.secrets.yamlfile. Seedocs/how-to/manage-secrets.mdand ADR 0009. Values committed in plaintext before SOPS was adopted haven't been rotated — known, tracked gap, seedocs/reference/architecture.md.
Backups
Stateful data (lldap accounts, Authelia sessions/2FA, the Forgejo database and
its git repositories) is backed up nightly to object storage with restic — see
docs/how-to/manage-backups.md. Adding a new
stateful service does not back it up automatically; that's a manual step
documented there.
Documentation rules
Full explanation of the structure and where new content goes is in
docs/README.md — the short version: tutorials/ for
the linear from-scratch path, how-to/ for task recipes, reference/ for
dry current-state facts, explanation/ for concepts and trade-offs,
adr/ for immutable records of a specific decision. Don't mix a how-to
step with a concept explanation — link to explanation/ instead of
inlining it.