Generating Flux Manifests
Generating Flux Manifests
This guide walks through the complete workflow for generating a GitOps repository structure with Flux resources using Kure.
Overview
The workflow has four stages:
- Define the cluster topology using the domain model
- Select the Flux workflow engine
- Generate Flux resources and directory layout
- Write manifests to disk
Step 1: Define the Cluster
Use the fluent builder to define your cluster’s structure:
import "github.com/go-kure/kure/pkg/stack"
cluster := stack.NewClusterBuilder("production").
WithNode("infrastructure").
WithBundle("cert-manager").
WithApplication("cert-manager", certManagerConfig).
End().
End().
WithNode("applications").
WithBundle("web-tier").
WithApplication("frontend", frontendConfig).
WithApplication("api-gateway", apiConfig).
End().
End().
Build()Each bundle becomes a Flux Kustomization, and each application generates its Kubernetes manifests.
Step 2: Create the Flux Engine
import (
"github.com/go-kure/kure/pkg/stack/fluxcd"
"github.com/go-kure/kure/pkg/stack/layout"
)
engine := fluxcd.EngineWithConfig(
layout.KustomizationExplicit, // List files in kustomization.yaml
)Placement is configured per call on layout.LayoutRules.FluxPlacement (see Step 3
below). FluxUnset normalizes to FluxSeparate. See the
Flux Engine reference
for configuration options.
Step 3: Generate Resources with Layout
// Define layout rules
rules := layout.LayoutRules{
NodeGrouping: layout.GroupByName,
BundleGrouping: layout.GroupByName,
ApplicationGrouping: layout.GroupByName,
FilePer: layout.FilePerResource,
FluxPlacement: layout.FluxSeparate, // Flux resources in separate tree
}
// Generate layout with Flux resources integrated
ml, err := engine.CreateLayoutWithResources(cluster, rules)
if err != nil {
return errors.Wrap(err, "failed to create layout")
}Step 4: Write to Disk
err := layout.WriteManifest(ml, "./clusters")This produces a directory structure like:
clusters/
production/
infrastructure/
cert-manager/
cert-manager/
deployment.yaml
service.yaml
kustomization.yaml
kustomization.yaml # Flux Kustomization
applications/
web-tier/
frontend/
deployment.yaml
service.yaml
api-gateway/
deployment.yaml
service.yaml
kustomization.yaml # Flux KustomizationLayout Configuration
The Layout Engine supports multiple grouping and file organization strategies:
| Option | Values | Effect |
|---|---|---|
| NodeGrouping | GroupByName, GroupFlat | Create subdirectories per node or flatten |
| BundleGrouping | GroupByName, GroupFlat | Create subdirectories per bundle or flatten |
| ApplicationGrouping | GroupByName, GroupFlat | Create subdirectories per app or flatten |
| FilePer | FilePerResource, FilePerKind | One file per resource or group by kind |
| FluxPlacement | FluxSeparate, FluxIntegratedPerLayout, FluxIntegratedPerBundle | Separate dir; a Flux CR per layout node; or Flux CRs at bundle boundaries with children as directories |
Umbrella Bundles — Readiness Aggregation
A bundle with non-empty Children becomes an umbrella: Flux will only mark
its Kustomization Ready once every child Kustomization is Ready. The Flux
engine enforces this by:
- Forcing
spec.wait: trueon the umbrella’s Kustomization - Prepending an auto
spec.healthChecksentry for each direct child
The resulting umbrella Kustomization aggregates child readiness regardless of how many children there are, giving external consumers a single stable anchor:
umbrella := &stack.Bundle{
Name: "platform",
Children: []*stack.Bundle{
{Name: "platform-infra"},
{Name: "platform-services"},
{Name: "platform-apps"},
},
}apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: platform
namespace: flux-system
spec:
wait: true
healthChecks:
- apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
name: platform-infra
namespace: flux-system
- apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
name: platform-services
namespace: flux-system
- apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
name: platform-apps
namespace: flux-system
# ...rest of specUser-supplied HealthChecks on the umbrella bundle are appended AFTER the
auto entries. Setting Wait: false on a bundle that has Children is
rejected at validation time.
Umbrella children must be standalone — a bundle cannot simultaneously be
the Bundle of a stack.Node and appear in another bundle’s Children.
stack.ValidateCluster rejects any such overlap before resource generation.
Disk layout
In FluxIntegratedPerLayout placement, the umbrella children’s Flux Kustomization CRs
live alongside the parent’s, and the parent’s kustomization.yaml references
each child via the CR file (not the child subdirectory):
clusters/production/apps/
platform/ # umbrella bundle directory
flux-system-kustomization-platform.yaml # umbrella self CR (wait+HC)
flux-system-kustomization-platform-infra.yaml # child CR (placed at parent)
flux-system-kustomization-platform-apps.yaml # child CR (placed at parent)
kustomization.yaml # references the CR files
platform-infra/ # umbrella child subdirectory
workload-*.yaml
kustomization.yaml # workloads only, no Flux CRs
platform-apps/
workload-*.yaml
kustomization.yamlThe child subdirectories contain only their workload manifests and a
per-directory kustomization.yaml listing those workloads. They do not
contain any flux-system-kustomization-*.yaml files — those live in the
parent directory, so Flux applies them once via the parent’s Kustomization.
In FluxSeparate placement, all Kustomization CRs (the umbrella’s own plus
every descendant) are written to the shared flux-system/ directory as a
flat list.
Augmenter-Added Child Layouts
A LayoutAugmenter can attach sub-ManifestLayout children to a per-app layout after resource generation. In FluxIntegratedPerLayout mode kure automatically emits a Flux Kustomization CR for each eligible child.
Eligibility
A child layout receives a CR when:
!child.UmbrellaChildchild.ApplicationFileMode != AppFileSingle- The ancestor bundle’s
SourceRefhas bothKindandNameset (nil, empty struct, or missing either field is a hard error — aKustomizationwithoutspec.sourceRefis invalid)
CreateLayoutWithResources validates SourceRef completeness for all bundles before layout walking. Both the node bundle and every umbrella child bundle must have SourceRef.Kind and SourceRef.Name set when either inline mode (FluxIntegratedPerLayout or FluxIntegratedPerBundle) is active — both emit bundle/node CRs carrying a spec.sourceRef. FluxSeparate and non-Flux callers are unaffected.
Ordered reconciliation with DependsOn
Set ManifestLayout.DependsOn to a list of sibling layout names to express reconciliation order between hook groups. The integrator translates these into spec.dependsOn entries on the emitted CR:
preInstall := &layout.ManifestLayout{
Name: "nginx-00-pre-install",
// ...
}
hooks := &layout.ManifestLayout{
Name: "nginx-01-hooks",
DependsOn: []string{"nginx-00-pre-install"},
// ...
}This produces a nginx-01-hooks Kustomization CR with:
spec:
dependsOn:
- name: nginx-00-pre-installFlux reconciles nginx-01-hooks only after nginx-00-pre-install is healthy.
Naming uniqueness
The child layout Name becomes the Flux Kustomization CR’s metadata.name. Since all Kustomization CRs live in the flux-system namespace, names must be globally unique across the cluster. The recommended convention is {appName}-{hookGroupDir} (e.g. nginx-00-pre-install, nginx-01-hooks). Augmenters are responsible for enforcing this uniqueness.
Disk layout
clusters/production/prod/
flux-system-kustomization-nginx.yaml # app CR (placed at node level)
kustomization.yaml # references nginx CR
nginx/
flux-system-kustomization-nginx-00-pre-install.yaml
flux-system-kustomization-nginx-01-hooks.yaml
kustomization.yaml # references hook CRs
nginx-00-pre-install/
workload-*.yaml
kustomization.yaml
nginx-01-hooks/
workload-*.yaml
kustomization.yamlBootstrap
Generate Flux system bootstrap manifests. Two modes are available:
"flux-operator"(default) — emits a full Flux Operator install bundle (CRDs, Deployment, RBAC). Recommended for new clusters."gotk"— emits the legacy GitOps Toolkit component manifests directly.
When FluxMode is empty, it defaults to "flux-operator".
bootstrapConfig := &stack.BootstrapConfig{
Enabled: true,
FluxMode: "flux-operator", // or "gotk"; empty defaults to "flux-operator"
FluxVersion: "v2.8.2",
SourceRef: sourceRef,
}
objects, err := engine.GenerateBootstrap(bootstrapConfig, rootNode)Further Reading
- Stack - Domain model reference
- Flux Engine - Workflow engine reference
- Layout Engine - Directory organization reference