Files
homelab/k8s/bootstrap/README.md
T

129 lines
3.9 KiB
Markdown

# Homelab Bootstrap — Single-Cluster, GitOps-Ready
**Run once manually, GitOps forever after.**
This bootstrap breaks the ArgoCD ↔ Forgejo circular dependency by:
1. Installing infrastructure in correct dependency order
2. Pointing ArgoCD at a GitHub mirror initially
3. Cutting over to Forgejo once healthy
4. Using Helm for reproducible installs
5. Ensuring ArgoCD adopts (not duplicates) bootstrap resources
## Prerequisites
- Talos cluster running (terraform applied)
- kubectl configured (`KUBECONFIG` points 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
```bash
# 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
1. **DRY**: Helm values used by both bootstrap and ArgoCD
2. **Single Source of Truth**: Manifests match what ArgoCD will manage
3. **Idempotent**: Can re-run phases safely
4. **Adoption Ready**: Resources have `argocd.argoproj.io/sync-options: Prune=false`
5. **Dependency Ordered**: Each phase waits for previous to be Ready
## Phase Details
### Phase 1: Storage (Longhorn)
Installs Longhorn with:
- 3-node HA configuration
- Unified `longhorn` StorageClass (default)
- Special `longhorn-cnpg` StorageClass 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
1. Creates `forgejo-db` CNPG Cluster
2. Waits for cluster Ready (PostgreSQL accepting connections)
3. Installs Forgejo via Helm pointing at `forgejo-db-rw` service
4. 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.yaml`
- `phase4-argocd/root-app-github.yaml` (repoURL = GitHub)
ArgoCD **adopts** Phases 1-3 resources (no duplication) because manifests match.
### Phase 5: Cut Over to Forgejo
1. Push repo to Forgejo
2. Update root app `repoURL` from GitHub → Forgejo
3. ArgoCD re-syncs from Forgejo
**The circle is broken. GitHub mirror is now disaster recovery only.**
## Post-Bootstrap
All changes via Git:
```bash
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 forgejo`
- **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/`:
1. **DO NOT delete** existing resources (Longhorn data!)
2. Run refined bootstrap in "adoption mode" (no delete, just apply)
3. Verify ArgoCD shows "Synced" for all apps
4. Archive old bootstrap: `git mv k8s/bootstrap-local k8s/archive/bootstrap-local-v1`