Flux CD: Complete GitOps Toolkit for Kubernetes
Flux CD is a set of continuous and progressive delivery solutions for Kubernetes that are open and extensible. As a CNCF graduated project, Flux provides powerful GitOps capabilities for managing Kubernetes clusters. This comprehensive guide covers Flux installation, configuration, and production deployment patterns.
What is Flux CD?
Flux CD is a GitOps toolkit consisting of specialized components:
Core Components
- Source Controller: Manages Git repositories, Helm repositories, and S3 buckets
- Kustomize Controller: Reconciles Kustomize overlays
- Helm Controller: Manages Helm releases
- Notification Controller: Handles alerts and webhooks
- Image Automation: Automatically updates images
Key Features
- GitOps Native: Git as single source of truth
- Multi-Tenancy: Namespace isolation and RBAC
- Progressive Delivery: Canary and A/B deployments with Flagger
- Image Automation: Automatic image updates to Git
- Multi-Cluster: Manage multiple clusters from one control cluster
- Helm Support: Native Helm chart deployment
- Kustomize Support: Built-in Kustomize integration
- Webhook Receivers: Trigger reconciliation from Git events
- Notification System: Slack, Teams, Discord, webhook alerts
Flux vs. ArgoCD
| Feature | Flux CD | ArgoCD |
|---|---|---|
| Architecture | Distributed (multiple controllers) | Monolithic + API server |
| UI | External (Weave GitOps) | Built-in |
| Multi-tenancy | Native | Requires projects |
| Image automation | Built-in | Requires external tools |
| Helm | HelmRelease CRD | Native |
| Kustomize | Native | Native |
| Progressive delivery | Flagger integration | Argo Rollouts |
| Learning curve | Steeper | Gentler |
Installation
Prerequisites
# Install Flux CLI# macOSbrew install fluxcd/tap/flux
# Linuxcurl -s https://fluxcd.io/install.sh | sudo bash
# Windows (PowerShell)choco install flux
# Verify installationflux --versionCheck Prerequisites
# Check cluster requirementsflux check --pre
# Check for breaking changes before upgradeflux checkBootstrap Flux
GitHub
# Export GitHub tokenexport GITHUB_TOKEN=<your-token>
# Bootstrap Fluxflux bootstrap github \ --owner=my-org \ --repository=fleet-infra \ --branch=main \ --path=clusters/production \ --personal
# For organization repositoryflux bootstrap github \ --owner=my-org \ --repository=fleet-infra \ --team=platform-team \ --path=clusters/productionGitLab
# Export GitLab tokenexport GITLAB_TOKEN=<your-token>
# Bootstrap Fluxflux bootstrap gitlab \ --owner=my-group \ --repository=fleet-infra \ --branch=main \ --path=clusters/production \ --personal=falseGeneric Git
# For any Git providerflux bootstrap git \ --url=ssh://git@github.com/my-org/fleet-infra \ --branch=main \ --path=clusters/production \ --private-key-file=~/.ssh/fluxWhat Bootstrap Does
- Creates Git repository if it doesn’t exist
- Commits Flux component manifests to repository
- Configures Flux to sync from repository
- Deploys Flux components to cluster
- Sets up Git SSH key for authentication
Verify Installation
# Check Flux componentsflux check
# List all Flux resourceskubectl get all -n flux-system
# View Flux logsflux logs --all-namespaces --followRepository Structure
Recommended Structure
fleet-infra/├── clusters/│ ├── production/│ │ ├── flux-system/ # Flux components (managed)│ │ ├── infrastructure.yaml # Infrastructure sources/releases│ │ └── apps.yaml # Application sources/releases│ └── staging/│ └── ...├── infrastructure/│ ├── sources/│ │ ├── bitnami.yaml│ │ ├── jetstack.yaml│ │ └── prometheus.yaml│ └── controllers/│ ├── ingress-nginx/│ ├── cert-manager/│ └── sealed-secrets/└── apps/ ├── base/ │ └── app1/ │ ├── deployment.yaml │ ├── service.yaml │ └── kustomization.yaml └── production/ └── app1/ ├── kustomization.yaml └── patch.yamlGitRepository Source
apiVersion: source.toolkit.fluxcd.io/v1kind: GitRepositorymetadata: name: app-repo namespace: flux-systemspec: interval: 1m url: https://github.com/example/app-repo ref: branch: main secretRef: name: git-credentials ignore: | # exclude all /* # include charts directory !/charts/SSH Authentication
# Create SSH keyssh-keygen -t ed25519 -C "flux" -f flux-ssh
# Add to GitHub as deploy key
# Create secretflux create secret git flux-system \ --url=ssh://git@github.com/my-org/fleet-infra \ --private-key-file=flux-sshHTTPS with Token
# Create secret with tokenflux create secret git github-token \ --url=https://github.com/my-org/fleet-infra \ --username=git \ --password=$GITHUB_TOKENKustomize Deployments
Basic Kustomization
apiVersion: kustomize.toolkit.fluxcd.io/v1kind: Kustomizationmetadata: name: apps namespace: flux-systemspec: interval: 10m sourceRef: kind: GitRepository name: flux-system path: ./apps/production prune: true wait: true timeout: 5m validation: client healthChecks: - apiVersion: apps/v1 kind: Deployment name: app1 namespace: defaultMulti-Source Kustomization
apiVersion: kustomize.toolkit.fluxcd.io/v1kind: Kustomizationmetadata: name: app1 namespace: flux-systemspec: interval: 5m sourceRef: kind: GitRepository name: app-repo path: ./apps/app1/overlays/production prune: true
dependsOn: - name: infrastructure
patches: - patch: | apiVersion: apps/v1 kind: Deployment metadata: name: app1 spec: replicas: 5 target: kind: Deployment name: app1
postBuild: substitute: cluster_name: "production" domain: "example.com" substituteFrom: - kind: ConfigMap name: cluster-varsHelm Releases
HelmRepository
apiVersion: source.toolkit.fluxcd.io/v1beta2kind: HelmRepositorymetadata: name: bitnami namespace: flux-systemspec: interval: 1h url: https://charts.bitnami.com/bitnamiHelmRelease
apiVersion: helm.toolkit.fluxcd.io/v2beta1kind: HelmReleasemetadata: name: ingress-nginx namespace: ingress-nginxspec: interval: 30m chart: spec: chart: ingress-nginx version: '4.x' sourceRef: kind: HelmRepository name: bitnami namespace: flux-system interval: 12h
releaseName: ingress-nginx
install: remediation: retries: 3 crds: CreateReplace
upgrade: remediation: retries: 3 crds: CreateReplace
values: controller: replicaCount: 3 service: type: LoadBalancer resources: limits: cpu: 500m memory: 512Mi requests: cpu: 250m memory: 256Mi
valuesFrom: - kind: ConfigMap name: ingress-values valuesKey: values.yamlHelm from Git
apiVersion: source.toolkit.fluxcd.io/v1kind: GitRepositorymetadata: name: charts-repo namespace: flux-systemspec: interval: 5m url: https://github.com/example/helm-charts ref: branch: main---apiVersion: helm.toolkit.fluxcd.io/v2beta1kind: HelmReleasemetadata: name: my-app namespace: defaultspec: interval: 5m chart: spec: chart: ./charts/my-app sourceRef: kind: GitRepository name: charts-repo namespace: flux-system values: replicaCount: 3Image Automation
Image Repository
apiVersion: image.toolkit.fluxcd.io/v1beta2kind: ImageRepositorymetadata: name: my-app namespace: flux-systemspec: image: ghcr.io/example/my-app interval: 1m secretRef: name: ghcr-credentialsImage Policy
apiVersion: image.toolkit.fluxcd.io/v1beta2kind: ImagePolicymetadata: name: my-app namespace: flux-systemspec: imageRepositoryRef: name: my-app policy: semver: range: '>=1.0.0 <2.0.0' # Or use pattern # policy: # pattern: '^main-[a-f0-9]+-(?P<ts>[0-9]+)' # extract: '$ts'Image Update Automation
apiVersion: image.toolkit.fluxcd.io/v1beta1kind: ImageUpdateAutomationmetadata: name: my-app namespace: flux-systemspec: interval: 1m sourceRef: kind: GitRepository name: flux-system git: checkout: ref: branch: main commit: author: email: fluxcdbot@users.noreply.github.com name: fluxcdbot messageTemplate: | Automated image update
Automation name: {{ .AutomationObject }}
Files: {{ range $filename, $_ := .Updated.Files -}} - {{ $filename }} {{ end -}}
Objects: {{ range $resource, $_ := .Updated.Objects -}} - {{ $resource.Kind }} {{ $resource.Name }} {{ end -}}
Images: {{ range .Updated.Images -}} - {{.}} {{ end -}} update: path: ./apps/production strategy: SettersImage Marker in Manifests
apiVersion: apps/v1kind: Deploymentmetadata: name: my-appspec: template: spec: containers: - name: app image: ghcr.io/example/my-app:1.0.0 # {"$imagepolicy": "flux-system:my-app"}Notifications
Notification Provider
apiVersion: notification.toolkit.fluxcd.io/v1beta1kind: Providermetadata: name: slack namespace: flux-systemspec: type: slack channel: flux-notifications secretRef: name: slack-webhook---apiVersion: v1kind: Secretmetadata: name: slack-webhook namespace: flux-systemstringData: address: https://hooks.slack.com/services/YOUR/WEBHOOK/URLAlert
apiVersion: notification.toolkit.fluxcd.io/v1beta1kind: Alertmetadata: name: on-call-alerts namespace: flux-systemspec: providerRef: name: slack eventSeverity: error eventSources: - kind: GitRepository name: '*' - kind: Kustomization name: '*' - kind: HelmRelease name: '*' suspend: falseWebhook Receiver
apiVersion: notification.toolkit.fluxcd.io/v1kind: Receivermetadata: name: github-receiver namespace: flux-systemspec: type: github events: - "ping" - "push" secretRef: name: webhook-token resources: - apiVersion: source.toolkit.fluxcd.io/v1 kind: GitRepository name: flux-systemConfigure webhook in GitHub:
- URL:
https://flux.example.com/hook/<receiver-url-path> - Content type:
application/json - Secret: From
webhook-tokensecret
Multi-Cluster Management
Cluster Configuration
# Structure for multiple clustersfleet-infra/├── clusters/│ ├── production-us-east/│ ├── production-eu-west/│ └── staging/└── infrastructure/ ├── bases/ └── overlays/ ├── production/ └── staging/Cluster-Specific Kustomization
apiVersion: kustomize.toolkit.fluxcd.io/v1kind: Kustomizationmetadata: name: infrastructure namespace: flux-systemspec: interval: 10m sourceRef: kind: GitRepository name: flux-system path: ./infrastructure/overlays/production prune: true postBuild: substitute: cluster_region: "us-east-1" cluster_provider: "aws" substituteFrom: - kind: ConfigMap name: cluster-settingsFlux with Amazon EKS
EKS-Specific Configuration
apiVersion: kustomize.toolkit.fluxcd.io/v1kind: Kustomizationmetadata: name: aws-load-balancer-controller namespace: flux-systemspec: interval: 1h sourceRef: kind: GitRepository name: flux-system path: ./infrastructure/aws-load-balancer-controller prune: true healthChecks: - apiVersion: apps/v1 kind: Deployment name: aws-load-balancer-controller namespace: kube-system patches: - patch: | - op: replace path: /spec/template/spec/containers/0/args value: - --cluster-name=eks-production - --aws-region=us-east-1 target: kind: Deployment name: aws-load-balancer-controllerIRSA for Flux
# Create IAM role for Fluxeksctl create iamserviceaccount \ --name flux-image-reflector \ --namespace flux-system \ --cluster eks-production \ --attach-policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly \ --approveECR Integration
apiVersion: v1kind: Secretmetadata: name: ecr-credentials namespace: flux-systemtype: OpaquestringData: username: AWS password: <ECR_TOKEN>---apiVersion: image.toolkit.fluxcd.io/v1beta2kind: ImageRepositorymetadata: name: my-app namespace: flux-systemspec: image: 123456789.dkr.ecr.us-east-1.amazonaws.com/my-app interval: 1m secretRef: name: ecr-credentialsMonitoring Flux
Metrics
# ServiceMonitor for FluxapiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: flux-system namespace: flux-systemspec: endpoints: - interval: 30s port: http-prom selector: matchLabels: app: source-controllerGrafana Dashboards
Import official Flux dashboards:
- Flux Cluster Stats: Dashboard ID 16213
- Flux Control Plane: Dashboard ID 16214
Secrets Management
Sealed Secrets
# Install sealed-secrets controllerflux create source helm sealed-secrets \ --url=https://bitnami-labs.github.io/sealed-secrets \ --interval=1h
flux create helmrelease sealed-secrets \ --source=HelmRepository/sealed-secrets \ --chart=sealed-secrets \ --target-namespace=kube-system \ --create-target-namespace=true
# Create sealed secretecho -n mypassword | kubectl create secret generic mysecret \ --dry-run=client \ --from-file=password=/dev/stdin \ -o yaml | \ kubeseal -o yaml > mysecret-sealed.yamlSOPS
# Install SOPSflux create secret sops age-key \ --namespace=flux-system \ --from-file=age.agekey=/path/to/age.key
# Create encrypted secretcat <<EOF | sops -e --age <public-key> /dev/stdin > secret.enc.yamlapiVersion: v1kind: Secretmetadata: name: mysecretstringData: password: mysecretpasswordEOFConfigure decryption:
# Kustomization with SOPSspec: decryption: provider: sops secretRef: name: age-keyBest Practices
Repository Organization
- Separate concerns: Infrastructure vs applications
- Environment isolation: Separate directories per environment
- Base + overlays: Use Kustomize bases and overlays
- Helm charts: Keep charts in separate repos
Health Checks
spec: healthChecks: - apiVersion: apps/v1 kind: Deployment name: app1 namespace: default - apiVersion: apps/v1 kind: StatefulSet name: database namespace: defaultDependency Management
spec: dependsOn: - name: infrastructure - name: crdsSuspend Reconciliation
# Suspend specific resourceflux suspend kustomization apps
# Resumeflux resume kustomization apps
# Suspend allflux suspend kustomization --allTroubleshooting
# Check all Flux resourcesflux get all
# Specific resource typesflux get sources gitflux get kustomizationsflux get helmreleases
# Reconcile immediatelyflux reconcile source git flux-systemflux reconcile kustomization apps
# View logsflux logs --all-namespaces --follow
# Export current stateflux export source git flux-systemflux export kustomization apps
# Trace resourceflux trace deployment/my-appConclusion
Flux CD provides a comprehensive, Kubernetes-native GitOps solution with powerful features for managing infrastructure and applications. Its modular architecture, native Kubernetes integration, and extensive tooling make it an excellent choice for teams adopting GitOps practices.
Master GitOps with Flux through our Kubernetes training programs. Contact us for customized training.