Flux Bootstrap Procedure¶
This document outlines the clean bootstrap procedure for FluxCD on new clusters, based on lessons learned from staging environment issues.
For how application repositories connect to Flux (and where manifests live), see kubernetes/docs/gitops-apps.md.
Overview¶
FluxCD bootstrap has a dependency challenge: flux pods need calico networking to start, but calico is deployed by flux kustomizations. This procedure addresses this chicken-and-egg problem.
Prerequisites¶
- Kubernetes cluster is running and accessible via kubectl
- Flux CLI is installed (
brew install fluxcd/tap/flux) - GitHub token with repository access
- SOPS/Age keys for secret decryption
- SSH keys for repository access
Bootstrap Steps¶
1. Pre-Bootstrap: Apply Critical Infrastructure¶
This step applies to the Ansible-provisioned on-prem path, not to EKS
The Calico and common/ steps below come from the on-prem cluster path (see
Ansible). They do not apply to the EKS clusters:
- EKS (
prod-cpt-aws,staging-cpt-aws) gets its CNI and kube-proxy from managed EKS add-ons provisioned by Terraform (aws_eks_addon.vpc_cni,aws_eks_addon.kube_proxyinterraform/aws/_modules/eks/_eks-addons.tf), so there is no CNI ordering problem to break. Skip to step 2 and runflux bootstrapdirectly. - A
common/overlay exists only forstaging-cpt-aws, and currently contains cert-manager only — there is nocommon/underprod-cpt-aws. Runningkubectl apply -k common/from the prod overlay will fail.
Before flux bootstrap on the on-prem cluster, manually apply the common resources to break the dependency cycle:
# Navigate to the overlay directory
cd kubernetes/overlays/<environment>/
# Apply common resources first (cert-manager; nginx/ghcr entries are currently commented out)
kubectl apply -k common/
# Wait for calico to be ready
kubectl wait --for=condition=Ready pods -l k8s-app=calico-node -n kube-system --timeout=300s
kubectl wait --for=condition=Ready pods -l k8s-app=calico-kube-controllers -n kube-system --timeout=300s
# Verify CoreDNS is working (restart if needed)
kubectl rollout status deployment/coredns -n kube-system
# If DNS issues: kubectl rollout restart deployment/coredns -n kube-system
2. Bootstrap Flux¶
# Bootstrap flux with the environment-specific path
flux bootstrap github \
--owner=example-org \
--repository=platform-infra \
--branch=main \
--path=kubernetes/overlays/<environment> \
--personal
3. Configure SOPS Decryption¶
# Create age key secret for SOPS decryption
kubectl create secret generic sops-keys \
--namespace=flux-system \
--from-file=identity.agekey=<path-to-age-key>
4. Verify Bootstrap¶
# Check flux status
flux check
# Verify all kustomizations
flux get kustomizations
# Check that all controllers are running
kubectl get pods -n flux-system
Environment-Specific Paths¶
- Production:
kubernetes/overlays/prod-cpt-aws - Staging:
kubernetes/overlays/staging-cpt-aws
Only two overlays exist today
kubernetes/overlays/ contains prod-cpt-aws and staging-cpt-aws only. An
office-cpt-onprem overlay is referenced in some older material but is not present in this
repository; treat any reference to it as historical.
Common Issues & Solutions¶
1. Pods Stuck in ContainerCreating¶
Symptom: Flux pods show ContainerCreating status indefinitely
Cause: Calico networking not ready
Solution: Apply common resources first (step 1)
2. DNS Resolution Failures¶
Symptom: failed to checkout and determine revision: dial tcp: lookup github.com: server misbehaving
Cause: CoreDNS issues after calico installation
Solution: Restart CoreDNS: kubectl rollout restart deployment/coredns -n kube-system
3. Kustomization Build Failures¶
Symptom: may not add resource with an already registered id
Cause: Multiple kustomization files with same metadata name
Solution: Use single kustomization approach with patch references
4. Namespace Termination Issues¶
Symptom: flux-system namespace stuck in Terminating
Solution: Remove finalizers from stuck resources:
kubectl patch kustomization <name> -n flux-system -p '{"metadata":{"finalizers":null}}' --type=merge
Ansible Automation¶
Current Roles¶
k8s-fluxcd-bootstrap: Handles flux bootstrapk3s-sops-age-secret: Configures SOPS decryption
Recommended Automation Updates¶
- Add pre-bootstrap step to apply common resources
- Add health checks for calico and CoreDNS
- Add retry logic for DNS resolution issues
- Validate kustomization structure before bootstrap
Production Checklist¶
- Cluster is ready and kubectl configured
- Environment-specific overlay exists (
kubernetes/overlays/prod-cpt-aws/) - Common resources validated (
kubectl kustomize common/) - Apps kustomizations validated (
kubectl kustomize apps/) - SOPS age key available
- GitHub token with repository access
- SSH deploy key configured in GitHub repository
- DNS resolution working from cluster pods
Directory Structure¶
kubernetes/overlays/<environment>/
├── flux-system/
│ ├── kustomization.yaml
│ ├── gotk-components.yaml
│ ├── gotk-sync.yaml
│ └── flux-kustomizations.yaml
├── common/
│ └── kustomization.yaml (→ ../../../_common/*)
└── apps/
└── <application>/
├── kustomization.yaml (single file, references base + patches)
└── <component>/
└── *.yaml (patch files only)
Key Principles¶
- Single source of truth: One flux bootstrap per cluster
- Dependency order: Common infrastructure before flux
- Simple kustomizations: Avoid nested kustomization references
- Health validation: Always verify critical components before proceeding
- Idempotent operations: All steps should be safely repeatable
Recovery Procedure¶
If flux becomes completely broken:
-
Clean up flux-system namespace:
-
Remove flux finalizers if namespace is stuck:
-
Follow bootstrap procedure from step 1 — on EKS, that means starting at step 2; the pre-bootstrap step is on-prem only.
-
Restore SOPS decryption.
flux bootstrapdoes not recreate the decryption secret. Re-create it before the rootKustomizationcan reconcile anything encrypted — see Secrets (SOPS + Age) for which secret name each cluster expects (sops-ageat the cluster root,sops-keysfor the per-app patch) and SOPS decryption troubleshooting.
This ensures a clean, reproducible flux deployment for production environments.
Re-bootstrapping requires the Age private key
Without it the cluster cannot decrypt anything and this procedure cannot complete. Confirm you
have access to the key before deleting flux-system. See
Secret stores for custody.
Not yet rehearsed
This procedure has not been recorded as tested against a live cluster. Treat timings as unverified and prefer rehearsing on staging first. Record the outcome here once it has been exercised.