All Articles

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.

DevOpsBoysMar 30, 20265 min read
Share:Tweet

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)
  • kubectl configured and working
  • GitHub account and personal access token
  • flux CLI installed

Step 1: Install the Flux CLI

macOS/Linux:

bash
curl -s https://fluxcd.io/install.sh | sudo bash

Windows (via Chocolatey):

bash
choco install flux

Verify:

bash
flux --version
# flux version 2.3.0

Step 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:

  1. Go to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens
  2. Give it Contents: Read and write and Metadata: Read permissions on your new repo

Export token as env var:

bash
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
export GITHUB_USER=your-github-username

Step 3: Check Cluster Compatibility

bash
flux check --pre

This 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:

bash
flux bootstrap github \
  --owner=$GITHUB_USER \
  --repository=my-cluster-config \
  --branch=main \
  --path=./clusters/my-cluster \
  --personal

What happens:

  1. Flux creates flux-system namespace in your cluster
  2. Installs source-controller, kustomize-controller, helm-controller, notification-controller
  3. Commits Flux's own manifests to clusters/my-cluster/flux-system/ in your repo
  4. Flux immediately starts reconciling from that path

Verify:

bash
kubectl get pods -n flux-system
# source-controller-xxx      Running
# kustomize-controller-xxx   Running
# helm-controller-xxx        Running
# notification-controller-xxx Running
 
flux get all

GitOps 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):

bash
# 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.yaml

This creates:

yaml
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-app

Commit and push — Flux reconciles within 1 minute.

Step 6: Create a Kustomization

A Kustomization tells Flux which path in the GitRepository to apply:

yaml
# 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: 2m

prune: 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:

yaml
# 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/podinfo

Create a HelmRelease:

yaml
# 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: 64Mi

Commit, push, and watch Flux install the Helm chart automatically:

bash
flux get helmreleases -A
watch kubectl get pods -n podinfo

Step 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:

yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml

overlays/production/kustomization.yaml:

yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: patch-replicas.yaml

Each 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:

yaml
# 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: Setters

Now in your deployment YAML, mark the image tag for Flux to update:

yaml
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

bash
# 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-app

Troubleshooting Common Issues

Kustomization stuck in Reconciling:

bash
flux describe kustomization my-app
# Check for YAML errors or missing resources

HelmRelease failing:

bash
flux describe helmrelease podinfo
kubectl describe helmrelease podinfo -n flux-system
# Look at "Status.Conditions" for error messages

Git repo not accessible:

bash
flux describe source git my-app
# Check for authentication errors — re-create secret if needed

Running FluxCD on production Kubernetes? Deploy on DigitalOcean Kubernetes — managed K8s with $200 free credits, perfect for GitOps setups.

Newsletter

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

Comments