Skip to content

Bootstrapping Flux

Flux watches a Git repository and continuously reconciles your cluster toward the state declared there. Any drift — a manually applied change, a deleted resource — gets reverted. Infrastructure updates land by pushing to Git, not by running kubectl.

  • A running cluster (k0s or equivalent)
  • The flux CLI installed
  • A GitHub personal access token with Contents and Administration read/write permissions
  • Secrets initialized via kb secrets init (creates the encrypted .env.json file)

kb flux bootstrap reads your GitHub token from the decrypted secrets file and delegates to flux bootstrap github. It never touches your shell environment directly — the token is injected as GITHUB_TOKEN into the child process only.

Terminal window
kb flux bootstrap \
--secrets-file /path/to/.env.json \
--owner RyanGreenup \
--repository kubernetes-template \
--branch main \
--path clusters/vale

The --path flag scopes the cluster sync to a subdirectory of the repository. Multiple clusters can share one repo by using different paths.

Bootstrap is idempotent. Run it again to upgrade Flux controllers or recover from a broken state.

Flux commits two files to <path>/flux-system/ in your repository:

  • gotk-components.yaml — all Flux controller deployments, CRDs, and RBAC
  • gotk-sync.yaml — the root GitRepository and Kustomization that point Flux back at itself

The gotk-sync.yaml for the local cluster looks like this:

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 1m0s
ref:
branch: main
secretRef:
name: flux-system
url: ssh://git@github.com/RyanGreenup/kubernetes-template
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 10m0s
path: ./clusters/local
prune: true
sourceRef:
kind: GitRepository
name: flux-system

Flux pulls from Git every minute and reconciles every ten. The prune: true flag means resources removed from Git get deleted from the cluster.

After bootstrap, pull from the remote repository — Flux commits to the remote, not your local working copy.

The root Kustomization applies everything under clusters/local/. Place additional Kustomization manifests there to point Flux at your overlay directories. The infrastructure Kustomization for the local cluster uses SOPS decryption:

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: infrastructure
namespace: flux-system
spec:
decryption:
provider: sops
secretRef:
name: sops-age
interval: 10m0s
path: ./infrastructure/overlays/local
prune: true
sourceRef:
kind: GitRepository
name: flux-system

The sops-age secret must exist in the flux-system namespace before Flux attempts to apply any encrypted resources. Create it once per cluster from your age private key:

Terminal window
age-keygen | kubectl create secret generic sops-age \
--namespace=flux-system \
--from-file=age.agekey=/dev/stdin

Store the private key in KeePass or another secret store. Use one age key per cluster. SOPS supports multiple recipients, so you can encrypt each secret to several age keys at once if you need cross-cluster access.

The CLI exposes three additional subcommands for working with Flux after bootstrap:

Terminal window
# Suspend all kustomizations (e.g. before manual intervention)
kb flux pause
# Resume reconciliation
kb flux resume
# Trigger an immediate reconcile of the infrastructure kustomization
kb flux reconcile infrastructure

Suspending stops Flux from reverting manual changes. Always resume when done — leaving Flux paused means cluster drift goes undetected.

Check that all controllers are running and the initial sync succeeded:

Terminal window
flux check
flux get kustomizations -n flux-system

Both the flux-system and infrastructure kustomizations should show Applied revision: main@sha1:… with a recent timestamp. A False ready status with a message about missing secrets usually means the sops-age secret was not created before bootstrap ran — create it and trigger a reconcile.