How to Set Up FluxCD for GitOps on Kubernetes from Scratch (2026)
Step-by-step guide to installing FluxCD, bootstrapping it with GitHub, setting up Kustomizations and HelmReleases, and managing multi-environment deployments with GitOps.
FluxCD is a GitOps operator that keeps your Kubernetes cluster in sync with a Git repository. You push a change to Git ā Flux detects it, applies it to the cluster. No manual kubectl apply. No direct cluster access for developers. Infrastructure as code, actually enforced.
This guide gets you from zero to a production-ready FluxCD setup.
What You'll Build
By the end of this guide:
- Flux bootstrapped with GitHub repo
- Automatic sync every 1 minute
- HelmRelease for deploying an app via Helm
- Kustomization overlays for dev/staging/production
- Image automation to auto-update image tags on push
Prerequisites
- Kubernetes cluster (EKS, GKE, AKS, or local k3s)
kubectlconfigured and working- GitHub account and personal access token
fluxCLI installed
Step 1: Install the Flux CLI
macOS/Linux:
curl -s https://fluxcd.io/install.sh | sudo bashWindows (via Chocolatey):
choco install fluxVerify:
flux --version
# flux version 2.3.0Step 2: Create a GitHub Repository
Create a new repo on GitHub. This will be your GitOps repo ā it holds your Kubernetes manifests, not your application code.
Name it something like my-cluster-config or fleet-infra.
Generate a GitHub Personal Access Token:
- Go to GitHub ā Settings ā Developer settings ā Personal access tokens ā Fine-grained tokens
- Give it
Contents: Read and writeandMetadata: Readpermissions on your new repo
Export token as env var:
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
export GITHUB_USER=your-github-usernameStep 3: Check Cluster Compatibility
flux check --preThis verifies your cluster has the required Kubernetes version and API server access. Fix any warnings before proceeding.
Step 4: Bootstrap Flux
Bootstrap installs Flux in your cluster AND commits all Flux manifests to your Git repo. One command does both:
flux bootstrap github \
--owner=$GITHUB_USER \
--repository=my-cluster-config \
--branch=main \
--path=./clusters/my-cluster \
--personalWhat happens:
- Flux creates
flux-systemnamespace in your cluster - Installs source-controller, kustomize-controller, helm-controller, notification-controller
- Commits Flux's own manifests to
clusters/my-cluster/flux-system/in your repo - Flux immediately starts reconciling from that path
Verify:
kubectl get pods -n flux-system
# source-controller-xxx Running
# kustomize-controller-xxx Running
# helm-controller-xxx Running
# notification-controller-xxx Running
flux get allGitOps labs: KodeKloud has a dedicated FluxCD course with hands-on practice in real Kubernetes clusters.
Step 5: Add a GitRepository Source
Tell Flux to watch another repo (your app's Helm charts or manifests):
# Create a GitRepository source
flux create source git my-app \
--url=https://github.com/$GITHUB_USER/my-app \
--branch=main \
--interval=1m \
--export > ./clusters/my-cluster/my-app-source.yamlThis creates:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: my-app
namespace: flux-system
spec:
interval: 1m0s
ref:
branch: main
url: https://github.com/your-user/my-appCommit and push ā Flux reconciles within 1 minute.
Step 6: Create a Kustomization
A Kustomization tells Flux which path in the GitRepository to apply:
# clusters/my-cluster/my-app-kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app
namespace: flux-system
spec:
interval: 5m
path: ./deploy/base # path inside the GitRepository
prune: true # delete resources removed from Git
sourceRef:
kind: GitRepository
name: my-app
targetNamespace: my-app
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: my-app
namespace: my-app
timeout: 2mprune: true is important ā it means if you delete a manifest from Git, Flux deletes it from the cluster. This keeps Git as the single source of truth.
Step 7: Deploy a Helm Chart with HelmRelease
For apps packaged as Helm charts, use HelmRepository + HelmRelease:
Add a Helm chart repository:
# clusters/my-cluster/sources/podinfo-source.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: podinfo
namespace: flux-system
spec:
interval: 5m
url: https://stefanprodan.github.io/podinfoCreate a HelmRelease:
# clusters/my-cluster/apps/podinfo.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: podinfo
namespace: flux-system
spec:
interval: 5m
chart:
spec:
chart: podinfo
version: ">=6.0.0"
sourceRef:
kind: HelmRepository
name: podinfo
namespace: flux-system
targetNamespace: podinfo
install:
createNamespace: true
values:
replicaCount: 2
resources:
requests:
cpu: 100m
memory: 64MiCommit, push, and watch Flux install the Helm chart automatically:
flux get helmreleases -A
watch kubectl get pods -n podinfoStep 8: Multi-Environment Setup with Kustomize Overlays
Structure your repo for multiple environments:
clusters/
āāā dev/
ā āāā flux-system/
ā āāā apps.yaml # Kustomization pointing to deploy/overlays/dev
āāā staging/
ā āāā flux-system/
ā āāā apps.yaml
āāā production/
āāā flux-system/
āāā apps.yaml
deploy/
āāā base/
ā āāā deployment.yaml
ā āāā service.yaml
ā āāā kustomization.yaml
āāā overlays/
āāā dev/
ā āāā kustomization.yaml
ā āāā patch-replicas.yaml # replicas: 1
āāā staging/
ā āāā kustomization.yaml
ā āāā patch-replicas.yaml # replicas: 2
āāā production/
āāā kustomization.yaml
āāā patch-replicas.yaml # replicas: 5
base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yamloverlays/production/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: patch-replicas.yamlEach cluster runs its own Flux pointing to its overlay path. Changes to base/ propagate to all environments. Environment-specific changes stay isolated in overlays.
Step 9: Image Automation (Auto-update Tags)
Flux can watch a container registry and automatically update image tags in Git when a new image is pushed:
# clusters/my-cluster/image-automation.yaml
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: my-app
namespace: flux-system
spec:
image: ghcr.io/my-user/my-app
interval: 1m
---
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/v1beta1
kind: ImageUpdateAutomation
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 1m
sourceRef:
kind: GitRepository
name: flux-system
git:
checkout:
ref:
branch: main
commit:
author:
name: FluxBot
email: flux@example.com
messageTemplate: "chore: update images"
push:
branch: main
update:
path: ./clusters/my-cluster
strategy: SettersNow in your deployment YAML, mark the image tag for Flux to update:
image: ghcr.io/my-user/my-app:1.0.0 # {"$imagepolicy": "flux-system:my-app"}Push a new image ā Flux updates the tag in Git ā Flux applies the updated manifest to cluster. Full GitOps loop with zero manual steps.
Useful Flux Commands
# Force reconcile now (don't wait for interval)
flux reconcile kustomization my-app --with-source
# Check sync status
flux get kustomizations -A
flux get helmreleases -A
flux get sources git -A
# View Flux logs
flux logs --level=error --all-namespaces
# Suspend sync (for maintenance)
flux suspend kustomization my-app
# Resume sync
flux resume kustomization my-appTroubleshooting Common Issues
Kustomization stuck in Reconciling:
flux describe kustomization my-app
# Check for YAML errors or missing resourcesHelmRelease failing:
flux describe helmrelease podinfo
kubectl describe helmrelease podinfo -n flux-system
# Look at "Status.Conditions" for error messagesGit repo not accessible:
flux describe source git my-app
# Check for authentication errors ā re-create secret if neededRunning FluxCD on production Kubernetes? Deploy on DigitalOcean Kubernetes ā managed K8s with $200 free credits, perfect for GitOps setups.
Today I Fixed
Short real fixes from production ā posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam ā just practical engineering content.
Related Articles
Build a Complete CI/CD Pipeline with GitHub Actions + ArgoCD + EKS (2026)
A full project walkthrough ā from a simple app to a production-grade GitOps pipeline with automated builds, image scanning, and deployments to AWS EKS using ArgoCD.
Build a GitOps Pipeline with ArgoCD and Helm (2026 Project Walkthrough)
A complete GitOps setup with ArgoCD and Helm ā from installing ArgoCD to auto-deploying your app when you push to Git.
Helm vs Kustomize vs Jsonnet: Which Config Tool Should You Use in 2026?
Helm, Kustomize, and Jsonnet all solve Kubernetes config management differently. Here's an honest comparison to help you pick the right one for your team.