Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bootstrap and reconciliation

How Flux gets installed, what it takes over, and where the handoff from Ansible sits. Read this when a bootstrap fails part way, or when you need to know why something is applied rather than reconciled.

flux/ is the GitOps entrypoint. flux/cluster.yaml, the FluxInstance CR, is applied once by ansible/roles/flux_bootstrap, and Flux does not reconcile the flux/ directory itself, to avoid watching its own bootstrap.

Everything else under flux/ is reconciled. flux/infra/ks.yaml and flux/nodes/ks.yaml are the two Flux Kustomization CRs that point Flux at the matching repo-root directories, ./infra and ./nodes. Those two keep their full spec rather than being patched like every other ks.yaml, because flux/ has no kustomization.yaml of its own. Flux auto-generates one from cluster.yaml’s sync.path: flux, and adding a real one would pull cluster.yaml itself into reconciliation. The rest of the naming and layout rules are in Layout and naming.

Bootstrap sequence

Run by just ans k8s (ansible/playbooks/k8s.yml):

  1. k8s_cluster: render /etc/rancher/k3s/config.yaml from inventory, install k3s on the controller and then the workers, and fetch the kubeconfig into ansible/.generated/. The local-path StorageClass comes up with it, from k3s’s own bundled provisioner: monitoring, auth and nodes/kenaz.k8s/actual all bind PVCs on their first reconcile, and nothing in the Flux-managed tree can provision a StorageClass for itself. See Startup ordering.
  2. flux_bootstrap:
    1. Install the Flux Operator via Helm.
    2. Apply the flux-system/git-deploy-key Secret, with known_hosts built from GitHub’s published host keys rather than a blind ssh-keyscan.
    3. Create the namespaces the next step writes into, since Flux does not exist yet to declare them. Flux takes ownership of all of them on its first reconcile.
    4. Apply the two Secrets that exist to seed what Flux resolves for itself, and so cannot come from Flux: flux-system/sops-age and infisical-universal-auth in each tier namespace. See Secrets.
    5. Wait for the Flux Operator to be ready.
    6. Apply flux/cluster.yaml. Flux takes over from here.

There is no follow-up step. Everything past flux/cluster.yaml is Flux reconciling git.

Verify the handoff:

just fx sources    # the GitRepository is Ready at the pushed revision
just fx failing    # empty

The handoff, and the one line it never crosses back over. Green is the handoff point itself, the moment Ansible stops and Flux owns the cluster. The two Kustomization labels are paths under flux/.

ansible/playbooks/k8s.ymlFluxthis repositoryinfranodesflux/cluster.yaml is applied, never reconciled.Flux would otherwise watch its own bootstrapk8s_clusterk3s + local-pathflux_bootstrapFlux Operator(Helm)git-deploy-key, sops-age,infisical-universal-authapply flux/cluster.yaml(FluxInstance)  hands oversync.path flux/infra/ks.yamlnodes/ks.yaml






Every Kustomization carries a decryption block naming flux-system/sops-age, patched in once via infra/kustomization.yaml and nodes/kustomization.yaml. flux/infra/ks.yaml and flux/nodes/ks.yaml state it in full for the same reason they state everything else in full.

Binary assets and .sourceignore

source-controller applies a built-in exclude list when it packs the artifact, and that list drops *.png along with the other image and archive extensions. A PNG committed to the repository is therefore absent from the tree kustomize-controller builds, even though kustomize build and the pre-commit hooks pass locally. The failure surfaces as a build error naming a file that plainly exists:

kustomize build failed: ... open .../web-app-manifest-192x192.png: no such file or directory

.sourceignore in the repository root is read after those defaults, so a ! line re-includes what they excluded. It currently re-includes the Glance app icon under config/branding/. Add a line there when a component starts mounting any other binary, and confirm with just fx get that the consuming Kustomization reconciles.

Day-to-day

just fx get        # every Kustomization and its sync status
just fx failing    # only what isn't Ready
just fx reconcile  # force-reconcile everything, or `<name>` for one
just fx logs       # tail kustomize-controller

The full list is in Recipe reference.