Flux GitOps on Kubernetes: Bootstrap to HelmRelease
Flux CD GitOps: bootstrap on GitHub/GitLab, GitRepository + Kustomization, HelmRelease, image automation, multi-tenant repo layout, alerts and debugging.
💡 Quick Answer: Bootstrap Flux with
flux bootstrap github --owner=myorg --repository=fleet-infra --branch=main --path=clusters/production. Flux commits its own manifests to that repo and then syncs everything under the path. Use aGitRepository(source) +Kustomization(apply) for plain YAML/Kustomize, and aHelmRepository+HelmReleasefor charts. Force a sync withflux reconcile kustomization <name> --with-source.Key concept: Source controllers fetch artifacts (Git, OCI, Helm repos, buckets); reconcilers (
Kustomization,HelmRelease) apply them and prune what was removed from Git.Gotcha: Flux polls on
spec.interval. For push-triggered syncs add aReceiverwebhook instead of dropping every interval to1m.
Flux is a CNCF-graduated set of GitOps controllers. It is CLI/CRD-driven with no UI by default, lighter than Argo CD, and composable — run only the controllers you need.
Install the Flux CLI
brew install fluxcd/tap/flux # macOS
curl -s https://fluxcd.io/install.sh | sudo bash # Linux
flux --version
flux check --pre # validates cluster version/permissions before installBootstrap Flux
# GitHub (token needs repo admin to create the repo/deploy key)
export GITHUB_TOKEN=<token>
flux bootstrap github \
--owner=myorg \
--repository=fleet-infra \
--branch=main \
--path=clusters/production \
--personal # omit for an organisation repo
# GitLab
export GITLAB_TOKEN=<token>
flux bootstrap gitlab \
--owner=myorg \
--repository=fleet-infra \
--branch=main \
--path=clusters/production
flux check
kubectl get pods -n flux-system
# source-controller, kustomize-controller, helm-controller, notification-controllerBootstrap is idempotent — re-run it to upgrade Flux. Add --components-extra=image-reflector-controller,image-automation-controller to enable image automation. For any other Git server use flux bootstrap git --url=ssh://....
GitRepository + Kustomization
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: my-app
namespace: flux-system
spec:
interval: 1m
url: https://github.com/myorg/my-app
ref:
branch: main
secretRef:
name: git-credentials # private repos: flux create secret git ...
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization # Flux CRD — not the kustomize.config.k8s.io file
metadata:
name: my-app
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: my-app
path: ./k8s/overlays/production
prune: true # delete resources removed from Git
wait: true # health-check everything it applied
timeout: 3m
targetNamespace: production
dependsOn:
- name: infrastructure # e.g. CRDs / cert-manager firstHelmRepository + HelmRelease
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: podinfo
namespace: flux-system
spec:
type: oci
interval: 1h
url: oci://ghcr.io/stefanprodan/charts
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: podinfo
namespace: production
spec:
interval: 10m
chart:
spec:
chart: podinfo
version: "6.x"
sourceRef:
kind: HelmRepository
name: podinfo
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
remediateLastFailure: true # roll back if the last retry fails
values:
replicaCount: 3
valuesFrom:
- kind: ConfigMap
name: podinfo-values
optional: truehelm.toolkit.fluxcd.io/v2beta1/v2beta2 are removed in current Flux; migrate to v2 (same spec for most fields).
Image Automation
Scan a registry, pick the newest tag by policy, and commit it back to Git:
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: my-app
namespace: flux-system
spec:
image: ghcr.io/myorg/my-app
interval: 5m
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: my-app
namespace: flux-system
spec:
imageRepositoryRef:
name: my-app
policy:
semver:
range: ">=1.0.0"
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageUpdateAutomation
metadata:
name: my-app
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: flux-system
git:
checkout:
ref:
branch: main
commit:
author:
name: fluxbot
email: flux@example.com
messageTemplate: "chore: update {{ .AutomationObject.Name }} images"
push:
branch: main
update:
path: ./clusters/production
strategy: SettersMark the field to update with a setter comment — exact format matters:
containers:
- name: app
image: ghcr.io/myorg/my-app:1.2.3 # {"$imagepolicy": "flux-system:my-app"}Flux 2.7+ also serves these APIs as image.toolkit.fluxcd.io/v1.
Multi-Tenant Repository Layout
fleet-infra/
├── clusters/
│ ├── production/
│ │ ├── flux-system/ # written by bootstrap
│ │ ├── infrastructure.yaml # Kustomization -> ./infrastructure
│ │ └── tenants.yaml # Kustomization -> ./tenants, dependsOn infrastructure
│ └── staging/
├── infrastructure/ # cert-manager, ingress, monitoring
└── tenants/
├── team-a/ # namespace, RBAC, GitRepository + Kustomization
└── team-b/For per-environment differences keep an apps/base/ plus apps/staging/ and apps/production/ Kustomize overlays, and point each cluster’s apps Kustomization at its overlay path (path: ./apps/production).
Give each tenant Kustomization serviceAccountName: team-a so it applies with that team’s RBAC only (and start the kustomize-controller with --default-service-account to enforce it).
Notifications and Webhooks
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
name: slack
namespace: flux-system
spec:
type: slack
channel: deployments
secretRef:
name: slack-webhook
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
name: on-call
namespace: flux-system
spec:
providerRef:
name: slack
eventSeverity: error
eventSources:
- kind: Kustomization
name: "*"
- kind: HelmRelease
name: "*"
---
# Push-based trigger from GitHub instead of waiting for the poll interval
apiVersion: notification.toolkit.fluxcd.io/v1
kind: Receiver
metadata:
name: github
namespace: flux-system
spec:
type: github
events: ["push"]
secretRef:
name: webhook-token
resources:
- kind: GitRepository
name: flux-systemProgressive Delivery with Flagger
Flux applies whatever is in Git; for canaries add Flagger (same Flux project), which shifts traffic through your mesh or ingress and rolls back on bad metrics.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: flagger
namespace: flagger-system
spec:
interval: 1h
chart:
spec:
chart: flagger
sourceRef: {kind: HelmRepository, name: flagger, namespace: flux-system} # url: https://flagger.app
values:
meshProvider: nginx # istio, linkerd, contour, gatewayapi:v1, ...
metricsServer: http://prometheus.monitoring:9090
---
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: myapp
namespace: production
spec:
targetRef: {apiVersion: apps/v1, kind: Deployment, name: myapp}
service:
port: 8080
analysis:
interval: 1m
threshold: 5 # failed checks before rollback
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange: {min: 99}
interval: 1m
- name: request-duration
thresholdRange: {max: 500}
interval: 1mAn image bump committed by image automation now becomes a 10%→50% canary instead of a straight rolling update.
Flux CLI Cheat Sheet
flux get all -A # everything and its Ready status
flux get sources git
flux get kustomizations
flux get helmreleases -A
flux reconcile source git flux-system # fetch now
flux reconcile kustomization my-app --with-source # fetch + apply now
flux reconcile helmrelease podinfo -n production
flux diff kustomization my-app --path ./k8s/overlays/production
flux suspend kustomization my-app # pause during an incident
flux resume kustomization my-app
flux logs --follow --level=error
flux events --for Kustomization/my-app
flux export source git my-app > git-repository.yamlCommon Issues
Kustomization Not Ready — source not fetched (auth, branch name) or a health check failing. flux get sources git, then flux events --for Kustomization/<name>.
HelmRelease install retries exhausted — bad values or a failing hook. flux logs --kind=HelmRelease --name=podinfo -n production, fix values in Git, then flux reconcile helmrelease podinfo -n production --force.
no matches for kind "HelmRelease" in version "helm.toolkit.fluxcd.io/v2beta1" — the API was removed on upgrade; change to v2.
Image automation not committing — missing/wrong $imagepolicy marker, image controllers not installed, or the deploy key is read-only (bootstrap with --read-write-key).
Rollback — revert the commit in Git; Flux reconciles to it. For a HelmRelease, upgrade.remediation rolls back automatically on failure.
Best Practices
- Bootstrap once, manage Flux from Git — upgrades are a re-bootstrap or a PR
- Separate
infrastructure/from apps/tenants and order withdependsOn prune: true+wait: trueon every Kustomization- Pin chart versions (
6.xin dev, exact in prod) - Receivers + alerts instead of aggressive polling
- Encrypt secrets with SOPS (
spec.decryption.provider: sops) or use External Secrets
Frequently Asked Questions
What is Flux in GitOps?
Flux is a set of Kubernetes controllers that continuously pull desired state from Git (or OCI/Helm repositories) and reconcile the cluster to it. Git becomes the source of truth: a merge deploys, a revert rolls back, and manual drift is corrected on the next reconcile.
Flux vs Argo CD — which should I use?
Both are CNCF-graduated pull-based GitOps tools. Flux is CRD/CLI-native, modular and has first-class image automation and SOPS decryption; Argo CD offers a rich web UI, SSO/RBAC, AppProjects and ApplicationSets. See Argo CD GitOps and the Flux vs Argo CD comparison.
Is Flux CI/CD?
Flux is the CD half. CI (build, test, push image) stays in GitHub Actions, GitLab CI, Tekton, etc.; Flux picks up the new manifest commit — or the new image tag via image automation — and deploys it.
How do I force Flux to sync immediately?
flux reconcile source git flux-system fetches the latest commit; flux reconcile kustomization <name> --with-source fetches and applies in one step.
How do I roll back with Flux?
git revert the offending commit and push; Flux applies the previous state. HelmReleases can also roll back automatically via upgrade.remediation. Use flux suspend to freeze reconciliation while you investigate.

Recommended
Kubernetes Recipes — The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book →Learn by Doing
CopyPasteLearn — Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses →🎓 Deepen Your Skills — Hands-on Courses
Courses by CopyPasteLearn.com — Learn IT by Doing
