GitOps with Flux: Declarative Infrastructure and CD for Kubernetes

Table of Contents
GitOps is the operational model where Git is the single source of truth for your infrastructure and application desired state, and automated controllers inside your Kubernetes cluster continuously reconcile real-world state to match Git.
While ArgoCD provides a popular centralized Web UI, Flux (v2) is built on the GitOps Toolkit (GOTK): a modular set of Kubernetes Custom Resource Definitions (CRDs) and specialized controllers that run natively in-cluster. Flux excels at multi-tenant environments, automated image updating, and minimal resource footprint.
This guide walks through setting up production-grade GitOps with Flux v2, including Kustomize reconciliation, Helm releases, SOPS secrets encryption, and automated container image PRs.
1. Flux Architecture: The GitOps Toolkit
Flux breaks GitOps down into dedicated single-responsibility controllers:
Git Repository (GitHub / GitLab)
│
▼ (Pulls Git commit / tag / branch)
[ source-controller ]
│
┌──────────────────────┴──────────────────────┐
▼ (Raw manifests & Kustomize) ▼ (Helm Charts)
[ kustomize-controller ] [ helm-controller ]
│ │
▼ (Applies with Prune & Health Checks) ▼ (Executes Helm engine)
Kubernetes API Kubernetes API
▲ ▲
└──────────────────────┬──────────────────────┘
│ (Watches OCI Registry & commits new tags to Git)
[ image-automation-controller ]
source-controller: Fetches Git repos, Helm charts, OCI artifacts, and S3 buckets into cluster storage.kustomize-controller: Runs Kustomize overlays, decrypts SOPS secrets, applies manifests to the API server, and automatically prunes deleted resources.helm-controller: Declaratively manages the lifecycle of Helm chart releases.notification-controller: Emits events to Slack, Discord, or webhooks, and receives incoming webhooks from Git/OCI registries to trigger instant reconciliations.image-automation-controller: Detects new container tags in Docker registries and automatically commits updated image tags back to your Git repo.
2. Production Monorepo Repository Structure
A resilient multi-environment repository layout cleanly separates base infrastructure from tenant applications:
fleet-infra/
├── clusters/
│ ├── staging/
│ │ ├── flux-system/ # Bootstrap manifests
│ │ ├── infrastructure.yaml # Reconciles /infrastructure/staging
│ │ └── apps.yaml # Reconciles /apps/staging
│ └── production/
│ ├── flux-system/
│ ├── infrastructure.yaml
│ └── apps.yaml
├── infrastructure/
│ ├── base/
│ │ ├── ingress-nginx/
│ │ └── cert-manager/
│ └── staging/
│ └── kustomization.yaml
└── apps/
├── base/
│ └── payments-api/
└── staging/
├── kustomization.yaml
└── patch-replicas.yaml
3. Core Manifests: GitRepository & Kustomization
Defining the Source: GitRepository
# clusters/staging/fleet-source.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: fleet-infra
namespace: flux-system
spec:
interval: 1m
url: https://github.com/my-org/fleet-infra.git
ref:
branch: main
secretRef:
name: github-deploy-token
ignore: |
# Exclude non-manifest directories from checksums
/*
!/apps
!/infrastructure
Reconciling with Health Checks and Pruning: Kustomization
# clusters/staging/apps.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: staging-apps
namespace: flux-system
spec:
interval: 5m
path: ./apps/staging
prune: true # Automatically deletes removed k8s resources
wait: true # Blocks until all Pods pass readiness probes
timeout: 3m
sourceRef:
kind: GitRepository
name: fleet-infra
dependsOn:
- name: staging-infrastructure # Ensures ingress/CRDs exist before apps deploy
postBuild:
substitute:
ENVIRONMENT: "staging"
CLUSTER_DOMAIN: "stage.internal.net"
4. Managing Helm Releases with Drift Detection
Flux manages Helm charts natively without needing the Helm CLI installed locally:
# infrastructure/base/ingress-nginx/helm-release.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: ingress-nginx
namespace: flux-system
spec:
interval: 2h
url: https://kubernetes.github.io/ingress-nginx
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
interval: 15m
chart:
spec:
chart: ingress-nginx
version: "4.11.x"
sourceRef:
kind: HelmRepository
name: ingress-nginx
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
driftDetection:
mode: enabled # Automatically reverts manual edits back to chart values
values:
controller:
replicaCount: 3
resources:
requests:
cpu: 100m
memory: 128Mi
5. Secret Management: Encrypted In-Git with SOPS and Age
Never store plaintext secrets in Git. Flux natively decrypts SOPS encrypted YAML before applying it to the cluster:
Step 1: Encrypt the Secret with age
# Generate age private key for the cluster
age-keygen -o age.agekey
# Create in-cluster secret with the private key
kubectl create secret generic sops-age \
--namespace=flux-system \
--from-file=age.agekey
# Encrypt local Kubernetes secret
sops --encrypt --age $(cat age.agekey | grep public | cut -d: -f2 | xargs) \
--encrypted-regex '^(data|stringData)$' \
secret.yaml > apps/staging/secret.enc.yaml
Step 2: Configure Flux to Decrypt at Runtime
# In apps/staging/kustomization.yaml or root Kustomization CRD
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: staging-secrets
namespace: flux-system
spec:
interval: 10m
path: ./apps/staging
prune: true
sourceRef:
kind: GitRepository
name: fleet-infra
decryption:
provider: sops
secretRef:
name: sops-age # Matches private key stored in flux-system
6. Automated Image Updates (Continuous Deployment)
Flux can monitor your container registry and automatically commit new image tags directly to Git:
# apps/base/payments-api/image-policy.yaml
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: payments-api
namespace: flux-system
spec:
image: ghcr.io/my-org/payments-api
interval: 1m
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: payments-api
namespace: flux-system
spec:
imageRepositoryRef:
name: payments-api
policy:
semver:
range: '^1.x' # Auto-promotes minor and patch versions
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 1m
sourceRef:
kind: GitRepository
name: fleet-infra
git:
checkout:
ref:
branch: main
commit:
author:
email: fluxcdbot@users.noreply.github.com
name: fluxcdbot
messageTemplate: 'chore(cd): update payments-api to {{range .Updated.Images}}{{println .}}{{end}}'
push:
branch: main
update:
path: ./apps/staging
strategy: Setters
In your deployment YAML, add a comment setter:
spec:
template:
spec:
containers:
- name: api
image: ghcr.io/my-org/payments-api:1.4.2 # {"$imagepolicy": "flux-system:payments-api"}
When CI pushes ghcr.io/my-org/payments-api:1.4.3, Flux detects the tag, updates this exact line in Git, commits with chore(cd)..., pushes to main, and reconciles the new Pod in staging.
Flux v2 vs ArgoCD: Production Tradeoffs
| Feature | Flux v2 | ArgoCD |
|---|---|---|
| Architecture | Native Kubernetes controllers (GOTK) | Application CRD + Centralized Web Server |
| Web UI | Minimal / Weave GitOps (optional) | Rich interactive Dashboard built-in |
| Secret Decryption | Native SOPS & Age / KMS in controller | Requires ArgoCD plugin / external operator |
| Multi-Cluster | Lightweight agent pull per cluster | Central pull or push to remote clusters |
| Drift Correction | Continuous background self-healing | Continuous sync or manual sync buttons |
| Image Automation | Native in-repo auto-commit controller | Requires ArgoCD Image Updater (separate) |
You Might Also Like
Free In-Browser Developer Tools
Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.
Related Articles

Kubernetes Operators and Custom Resources: Automate Everything
Extend the Kubernetes control plane with Operators and Custom Resource Definitions (CRDs) to automate lifecycle management for complex stateful applications. Learn reconciliation loops, RBAC, testing, and production patterns.
Read more
Kubernetes Operators: Building Custom Controllers with the Operator SDK
A practical guide to building production Kubernetes operators with the Operator SDK and Kubebuilder in Go: CRD scaffolding, idempotent reconcile loops, finalizers, and envtest integration suites.
Read more
Implementing Zero-Trust Security in Kubernetes: The Complete Production Guide
Practical guide to eliminating flat-network perimeter security in Kubernetes: default-deny NetworkPolicies, SPIFFE/SPIRE workload identity, and strict mTLS.
Read more