Commit a02bee5 had both the Ingress rule and the cloudflared tunnel
hostname for authentik.riotpiao.com. Both were lost, so the tunnel
answered 404 for the hostname and the ingress had no matching rule.
The CoreDNS rewrite and the authentik-server Service were still intact;
only the two routing entries were missing. Re-add the Ingress (namespace
iam, service authentik-server port 80, TLS terminated by the wildcard
default-ssl-certificate) and the tunnel hostname.
Homelab Bootstrap — Single-Cluster, GitOps-Ready
Run once manually, GitOps forever after.
This bootstrap breaks the ArgoCD ↔ Forgejo circular dependency by:
- Installing infrastructure in correct dependency order
- Pointing ArgoCD at a GitHub mirror initially
- Cutting over to Forgejo once healthy
- Using Helm for reproducible installs
- Ensuring ArgoCD adopts (not duplicates) bootstrap resources
Prerequisites
- Talos cluster running (terraform applied)
- kubectl configured (
KUBECONFIGpoints at cluster) - Helm 3 installed
- SOPS age key at
~/.sops/homelab-age.key - GitHub mirror of this repo (for initial ArgoCD source)
Directory Structure
bootstrap/
├── phase1-storage/ # Longhorn via Helm
├── phase2-cnpg/ # CNPG operator via Helm
├── phase3-forgejo/ # Forgejo DB + Forgejo via Helm
├── phase4-argocd/ # ArgoCD via Helm → GitHub initially
└── phase5-cutover/ # Switch ArgoCD source to Forgejo
Usage
# From repo root:
./bootstrap.sh
# Or step-by-step:
./bootstrap.sh phase1 # Storage
./bootstrap.sh phase2 # CNPG
./bootstrap.sh phase3 # Forgejo
./bootstrap.sh phase4 # ArgoCD (GitHub mirror)
./bootstrap.sh phase5 # Cut over to Forgejo
Design Principles
- DRY: Helm values used by both bootstrap and ArgoCD
- Single Source of Truth: Manifests match what ArgoCD will manage
- Idempotent: Can re-run phases safely
- Adoption Ready: Resources have
argocd.argoproj.io/sync-options: Prune=false - Dependency Ordered: Each phase waits for previous to be Ready
Phase Details
Phase 1: Storage (Longhorn)
Installs Longhorn with:
- 3-node HA configuration
- Unified
longhornStorageClass (default) - Special
longhorn-cnpgStorageClass with postgres UID/GID mount options - CSI plugin tolerations for control-plane nodes
Source of Truth: phase1-storage/longhorn-values.yaml
Phase 2: CNPG Operator
Installs CloudNativePG operator with:
- CRD registration (blocks until CRD available)
- Webhook configuration
- Monitoring enabled
Source of Truth: phase2-cnpg/cnpg-values.yaml
Phase 3: Forgejo Database + Forgejo
- Creates
forgejo-dbCNPG Cluster - Waits for cluster Ready (PostgreSQL accepting connections)
- Installs Forgejo via Helm pointing at
forgejo-db-rwservice - Waits for Forgejo healthy
Source of Truth:
phase3-forgejo/forgejo-db.yaml(CNPG Cluster CR)phase3-forgejo/forgejo-values.yaml(Helm values)
Phase 4: ArgoCD (GitHub Mirror)
Installs ArgoCD via Helm, then applies root app-of-apps pointing at GitHub mirror.
This is the circle-breaker: ArgoCD syncs from GitHub (not Forgejo) initially.
Source of Truth:
phase4-argocd/argocd-values.yamlphase4-argocd/root-app-github.yaml(repoURL = GitHub)
ArgoCD adopts Phases 1-3 resources (no duplication) because manifests match.
Phase 5: Cut Over to Forgejo
- Push repo to Forgejo
- Update root app
repoURLfrom GitHub → Forgejo - ArgoCD re-syncs from Forgejo
The circle is broken. GitHub mirror is now disaster recovery only.
Post-Bootstrap
All changes via Git:
git commit -m "feat(app): add new service"
git push forgejo main
# ArgoCD auto-syncs
Troubleshooting
- Phase stuck? Check
kubectl get events -n <namespace> --sort-by='.lastTimestamp' - ArgoCD duplicating? Verify manifests match exactly (Helm values ↔ ArgoCD Application)
- Forgejo won't start? Check CNPG cluster Ready:
kubectl get cluster forgejo-db -n cicd - Can't push to Forgejo? Verify ingress-nginx healthy, DNS resolves
forgejo.riotpiao.com
Migration from Old Bootstrap
If you have existing k8s/bootstrap-local/:
- DO NOT delete existing resources (Longhorn data!)
- Run refined bootstrap in "adoption mode" (no delete, just apply)
- Verify ArgoCD shows "Synced" for all apps
- Archive old bootstrap:
git mv k8s/bootstrap-local k8s/archive/bootstrap-local-v1