Time guide
- Basic Flux health check: approximately 5 to 10 minutes
- Failed reconciliation investigation: approximately 10 to 30 minutes
- Render, dry-run, reconciliation and validation: approximately 15 to 45 minutes
The actual duration depends on repository size, deployment dependencies, controller health and the cause of any reconciliation failure.
Purpose#
This Runbook entry provides commands for checking and troubleshooting a Kubernetes environment managed by Flux.
The workflow follows a read-only-first approach:
- Confirm the Kubernetes context.
- Confirm Flux CLI and controller health.
- Inspect sources and managed resources.
- Review events and controller logs.
- Validate local manifests.
- Trigger reconciliation only when required.
- Confirm the resulting Git and cluster state.
Flux normally reconciles resources automatically according to their configured intervals. Manual reconciliation is useful after pushing an important change or while investigating a delayed or failed deployment.
Requirements#
- A working
kubectlinstallation - The Flux CLI
- A valid Kubernetes kubeconfig
- Access to the Flux-managed cluster
- Permission to read Flux custom resources
- Permission to reconcile resources when using the mutating commands
- A local clone of the GitOps repository for rendering and validation
Check the active Kubernetes context:
kubectl config current-contextList available contexts:
kubectl config get-contextsCheck the Flux CLI version:
flux version --clientDisplay the client and installed controller versions:
flux versionFlux uses the current Kubernetes context. Confirm the context before running any reconciliation command.
Read-only Flux health checks#
Check whether Flux is installed and healthy:
flux checkRun installation prerequisite checks:
flux check --preList Flux controllers:
kubectl get deployments \
-n flux-systemList controller pods:
kubectl get pods \
-n flux-system \
-o wideExpected controllers commonly include:
source-controller
kustomize-controller
helm-controller
notification-controllerImage automation installations may also include:
image-reflector-controller
image-automation-controllerCheck whether all Flux deployments have available replicas:
kubectl get deployments \
-n flux-system \
-o custom-columns='NAME:.metadata.name,READY:.status.readyReplicas,AVAILABLE:.status.availableReplicas,DESIRED:.spec.replicas'Flux resource overview#
List all Flux resources across all namespaces:
flux get all -AList all sources:
flux get sources all -AList Git sources:
flux get sources git -AList OCI sources:
flux get sources oci -AList Helm repositories:
flux get sources helm -AList Kustomizations:
flux get kustomizations -AList Helm releases:
flux get helmreleases -AList image automation resources:
flux get images all -AHealthy resources should normally show:
READY TrueThe status message should include the current applied or fetched revision.
Inspect a Git source#
List GitRepository resources:
kubectl get gitrepositories.source.toolkit.fluxcd.io -AInspect a particular Git source:
flux get source git <source-name> \
-n <namespace>Describe the source:
kubectl describe gitrepository <source-name> \
-n <namespace>Display the fetched revision:
kubectl get gitrepository <source-name> \
-n <namespace> \
-o jsonpath='{.status.artifact.revision}{"\n"}'Display the source readiness condition:
kubectl get gitrepository <source-name> \
-n <namespace> \
-o jsonpath='{.status.conditions[?(@.type=="Ready")].status}{"\n"}'Display the latest source status message:
kubectl get gitrepository <source-name> \
-n <namespace> \
-o jsonpath='{.status.conditions[?(@.type=="Ready")].message}{"\n"}'Possible source problems include:
- Repository authentication failure
- DNS or network connectivity problems
- Invalid repository URL
- Missing branch or tag
- TLS certificate failure
- Repository rate limiting
- Invalid or inaccessible artifact
Inspect a Kustomization#
List Kustomizations:
kubectl get kustomizations.kustomize.toolkit.fluxcd.io -AInspect a particular Kustomization:
flux get kustomization <kustomization-name> \
-n <namespace>Describe it:
kubectl describe kustomization <kustomization-name> \
-n <namespace>Display the last applied revision:
kubectl get kustomization <kustomization-name> \
-n <namespace> \
-o jsonpath='{.status.lastAppliedRevision}{"\n"}'Display its readiness state:
kubectl get kustomization <kustomization-name> \
-n <namespace> \
-o jsonpath='{.status.conditions[?(@.type=="Ready")].status}{"\n"}'Display the latest readiness message:
kubectl get kustomization <kustomization-name> \
-n <namespace> \
-o jsonpath='{.status.conditions[?(@.type=="Ready")].message}{"\n"}'Check its configured source and path:
kubectl get kustomization <kustomization-name> \
-n <namespace> \
-o custom-columns='NAME:.metadata.name,SOURCE_KIND:.spec.sourceRef.kind,SOURCE_NAME:.spec.sourceRef.name,PATH:.spec.path,PRUNE:.spec.prune,INTERVAL:.spec.interval'Inspect a HelmRelease#
List Helm releases:
kubectl get helmreleases.helm.toolkit.fluxcd.io -AInspect a particular release:
flux get helmrelease <release-name> \
-n <namespace>Describe the release:
kubectl describe helmrelease <release-name> \
-n <namespace>Display its readiness state:
kubectl get helmrelease <release-name> \
-n <namespace> \
-o jsonpath='{.status.conditions[?(@.type=="Ready")].status}{"\n"}'Display its latest readiness message:
kubectl get helmrelease <release-name> \
-n <namespace> \
-o jsonpath='{.status.conditions[?(@.type=="Ready")].message}{"\n"}'Display the currently applied chart revision:
kubectl get helmrelease <release-name> \
-n <namespace> \
-o jsonpath='{.status.history[0].chartVersion}{"\n"}'The exact status fields available can vary between Flux versions. If a JSONPath returns no value, inspect the resource:
kubectl get helmrelease <release-name> \
-n <namespace> \
-o yamlSanitise YAML output before sharing it publicly.
Find suspended resources#
List suspended Kustomizations:
kubectl get kustomizations.kustomize.toolkit.fluxcd.io -A \
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,SUSPENDED:.spec.suspend' |
grep trueList suspended Helm releases:
kubectl get helmreleases.helm.toolkit.fluxcd.io -A \
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,SUSPENDED:.spec.suspend' |
grep trueList suspended Git sources:
kubectl get gitrepositories.source.toolkit.fluxcd.io -A \
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,SUSPENDED:.spec.suspend' |
grep trueA suspended resource will not reconcile until resumed.
Do not resume a resource until the reason for suspension is understood.
Flux events#
Display recent Flux events:
flux events -ADisplay events for a particular resource:
flux events \
--for Kustomization/<kustomization-name> \
-n <namespace>Display Kubernetes events in the Flux namespace:
kubectl get events \
-n flux-system \
--sort-by=.metadata.creationTimestampDisplay warning events:
kubectl get events -A \
--field-selector=type=Warning \
--sort-by=.metadata.creationTimestampInvestigate messages relating to:
ArtifactFailed
BuildFailed
DependencyNotReady
GitOperationFailed
HealthCheckFailed
ReconciliationFailed
SourceUnavailable
StorageOperationFailed
UpgradeFailed
InstallFailedController logs#
Review source-controller logs:
kubectl logs \
-n flux-system \
deployment/source-controller \
--tail=200Review kustomize-controller logs:
kubectl logs \
-n flux-system \
deployment/kustomize-controller \
--tail=200Review helm-controller logs:
kubectl logs \
-n flux-system \
deployment/helm-controller \
--tail=200Review notification-controller logs:
kubectl logs \
-n flux-system \
deployment/notification-controller \
--tail=200Follow logs from a controller:
kubectl logs \
-n flux-system \
deployment/kustomize-controller \
--followStop log following with:
Ctrl+CSearch recent kustomize-controller logs for errors:
kubectl logs \
-n flux-system \
deployment/kustomize-controller \
--since=30m |
grep -iE 'error|failed|timeout'Compare Git and Flux revisions#
Display the latest local Git commit:
git log -1 \
--onelineDisplay the current local branch:
git branch --show-currentCheck the working tree:
git statusFetch remote changes:
git fetch --pruneCompare the local branch with its upstream:
git status -sbDisplay the Flux Git source revision:
kubectl get gitrepository <source-name> \
-n <namespace> \
-o jsonpath='{.status.artifact.revision}{"\n"}'Display the Kustomization’s applied revision:
kubectl get kustomization <kustomization-name> \
-n <namespace> \
-o jsonpath='{.status.lastAppliedRevision}{"\n"}'These values help determine whether:
- Git contains the intended change
- The Flux source has fetched the latest revision
- The Kustomization applied that revision
- The intended resources exist in the cluster
Local Kustomize render#
Change into the GitOps repository:
cd <path-to-gitops-repository>Render a Kustomize path using kubectl:
kubectl kustomize <path-to-kustomization>Save the rendered output for inspection:
kubectl kustomize <path-to-kustomization> \
> /tmp/rendered-manifests.yamlCheck that the output file is not empty:
test -s /tmp/rendered-manifests.yamlReview the output:
less /tmp/rendered-manifests.yamlSearch for expected resource kinds:
grep '^kind:' /tmp/rendered-manifests.yamlRemove the temporary file after review:
rm /tmp/rendered-manifests.yamlConfirm the path before removing files.
Client-side Kubernetes dry run#
Render and perform a client-side validation:
kubectl kustomize <path-to-kustomization> |
kubectl apply \
--dry-run=client \
-f -This does not submit the resources to the Kubernetes API server.
It can identify issues such as:
- Invalid YAML
- Missing required fields
- Unknown local object structure
- Invalid generated resources
- Duplicate resource definitions
Client-side dry runs do not validate all server-side policies or admission behaviour.
Server-side dry run#
Where supported and permitted, perform a server-side dry run:
kubectl kustomize <path-to-kustomization> |
kubectl apply \
--server-side \
--dry-run=server \
-f -This submits the resources to the API server for validation without persisting the changes.
A server-side dry run may evaluate:
- Kubernetes API validation
- Custom resource schemas
- Admission policies
- Webhooks
- Server-side field management
A server-side dry run communicates with the real cluster. Confirm the Kubernetes context before running it.
Flux build validation#
Build a Kustomization using Flux:
flux build kustomization <kustomization-name> \
--path <local-path> \
-n <namespace>Save the rendered Flux output:
flux build kustomization <kustomization-name> \
--path <local-path> \
-n <namespace> \
> /tmp/flux-rendered.yamlReview it:
less /tmp/flux-rendered.yamlRemove the temporary output after review:
rm /tmp/flux-rendered.yamlCheck the command options available in the installed Flux version:
flux build kustomization --helpManual Git source reconciliation#
State-changing operation
This command annotates the Flux resource and requests an immediate source reconciliation. It does not modify the Git repository.
Reconcile a Git source:
flux reconcile source git <source-name> \
-n <namespace>Example using the standard Flux source:
flux reconcile source git flux-system \
-n flux-systemSpecify a timeout:
flux reconcile source git <source-name> \
-n <namespace> \
--timeout=2mAfter reconciliation, inspect the fetched revision:
flux get source git <source-name> \
-n <namespace>Manual Kustomization reconciliation#
State-changing operation
This command requests an immediate Kustomization reconciliation. Flux may create, update or delete cluster resources according to the declared configuration and pruning settings.
Reconcile a Kustomization:
flux reconcile kustomization <kustomization-name> \
-n <namespace>Reconcile the source first, then the Kustomization:
flux reconcile kustomization <kustomization-name> \
-n <namespace> \
--with-sourceSpecify a timeout:
flux reconcile kustomization <kustomization-name> \
-n <namespace> \
--with-source \
--timeout=5mUse --with-source when Flux should fetch the latest source revision before applying the Kustomization.
Manual HelmRelease reconciliation#
State-changing operation
This command requests an immediate Helm reconciliation. Helm may install, upgrade, roll back or remediate a release according to the HelmRelease configuration.
Reconcile a HelmRelease:
flux reconcile helmrelease <release-name> \
-n <namespace>Reconcile its source first:
flux reconcile helmrelease <release-name> \
-n <namespace> \
--with-sourceSpecify a timeout:
flux reconcile helmrelease <release-name> \
-n <namespace> \
--with-source \
--timeout=10mAfter reconciliation:
flux get helmrelease <release-name> \
-n <namespace>Recommended reconciliation workflow#
1. Confirm the cluster context#
kubectl config current-context2. Confirm the Git working tree#
git status3. Confirm the intended commit exists#
git log -1 \
--oneline4. Render the manifests#
kubectl kustomize <path-to-kustomization> \
> /tmp/rendered-manifests.yaml5. Perform client-side validation#
kubectl apply \
--dry-run=client \
-f /tmp/rendered-manifests.yaml6. Perform server-side validation where appropriate#
kubectl apply \
--server-side \
--dry-run=server \
-f /tmp/rendered-manifests.yaml7. Confirm the change has been pushed#
git status -sb8. Reconcile the source#
flux reconcile source git <source-name> \
-n <namespace>9. Reconcile the Kustomization#
flux reconcile kustomization <kustomization-name> \
-n <namespace>10. Check the resulting status#
flux get kustomization <kustomization-name> \
-n <namespace>11. Check affected workloads#
kubectl get pods \
-n <workload-namespace> \
-o wide12. Review recent events#
kubectl get events \
-n <workload-namespace> \
--sort-by=.metadata.creationTimestamp13. Compare the applied revision#
kubectl get kustomization <kustomization-name> \
-n <namespace> \
-o jsonpath='{.status.lastAppliedRevision}{"\n"}'14. Remove the temporary rendered file#
rm /tmp/rendered-manifests.yamlTroubleshooting#
GitRepository is not ready#
Inspect the source:
kubectl describe gitrepository <source-name> \
-n <namespace>Check source-controller logs:
kubectl logs \
-n flux-system \
deployment/source-controller \
--since=30mCheck for warning events:
kubectl get events \
-n <namespace> \
--field-selector=type=Warning \
--sort-by=.metadata.creationTimestampCommon causes include:
- Invalid Git URL
- Missing branch or tag
- Invalid credentials
- Expired deploy key
- DNS failure
- Network timeout
- Repository access restriction
- TLS validation error
Do not display repository credential Secrets in public output.
Kustomization reports BuildFailed#
Inspect it:
kubectl describe kustomization <kustomization-name> \
-n <namespace>Render the configured local path:
kubectl kustomize <path-to-kustomization>Common causes include:
- Invalid YAML
- Missing resource file
- Incorrect relative path
- Duplicate resource
- Invalid patch target
- Unavailable Helm chart
- SOPS decryption failure
- Unsupported API version
Kustomization reports DependencyNotReady#
Display its dependencies:
kubectl get kustomization <kustomization-name> \
-n <namespace> \
-o jsonpath='{.spec.dependsOn}{"\n"}'Check all Kustomizations:
flux get kustomizations -AInvestigate the dependency before reconciling the dependent resource.
Kustomization reports HealthCheckFailed#
Describe the Kustomization:
kubectl describe kustomization <kustomization-name> \
-n <namespace>List the target workloads:
kubectl get deployments,statefulsets,daemonsets \
-n <workload-namespace>Review the relevant pods:
kubectl get pods \
-n <workload-namespace>Review recent warning events:
kubectl get events \
-n <workload-namespace> \
--field-selector=type=Warning \
--sort-by=.metadata.creationTimestampHelmRelease reconciliation fails#
Inspect the release:
kubectl describe helmrelease <release-name> \
-n <namespace>Review helm-controller logs:
kubectl logs \
-n flux-system \
deployment/helm-controller \
--since=30mCheck the Helm source:
flux get sources helm -ACheck the resulting workload:
kubectl get all \
-n <workload-namespace>Common causes include:
- Chart repository unavailable
- Invalid chart version
- Invalid values
- Failed Kubernetes hooks
- Unhealthy workloads
- Insufficient resources
- Storage provisioning failure
- Admission-policy rejection
Flux fetched the latest commit but the application did not change#
Check the Kustomization revision:
flux get kustomization <kustomization-name> \
-n <namespace>Check whether the expected path is configured:
kubectl get kustomization <kustomization-name> \
-n <namespace> \
-o jsonpath='{.spec.path}{"\n"}'Check dependencies:
kubectl get kustomization <kustomization-name> \
-n <namespace> \
-o jsonpath='{.spec.dependsOn}{"\n"}'If the application is controlled by a HelmRelease, reconcile and inspect the HelmRelease rather than assuming that source reconciliation alone will recreate or upgrade the release.
Post-reconciliation checks#
Check all Flux resources:
flux get all -ACheck Flux controller pods:
kubectl get pods \
-n flux-systemCheck the affected namespace:
kubectl get all \
-n <workload-namespace>Check persistent volume claims:
kubectl get persistentvolumeclaims \
-n <workload-namespace>Check ingress:
kubectl get ingress \
-n <workload-namespace>Check warning events:
kubectl get events \
-n <workload-namespace> \
--field-selector=type=Warning \
--sort-by=.metadata.creationTimestampConfirm the applied Git revision:
kubectl get kustomization <kustomization-name> \
-n <namespace> \
-o jsonpath='{.status.lastAppliedRevision}{"\n"}'Safety notes#
Read-only commands in this entry include:
flux check
flux get
flux events
kubectl get
kubectl describe
kubectl logs
kubectl kustomize
git status
git log
git fetchThe following commands initiate reconciliation and may change cluster resources:
flux reconcile source git
flux reconcile kustomization
flux reconcile helmreleaseKustomizations with pruning enabled may delete managed resources that no longer exist in Git.
HelmRelease reconciliation may install, upgrade, remediate or roll back workloads according to its declared configuration.
Before manual reconciliation:
- Confirm the Kubernetes context
- Confirm the intended Git branch
- Review the Git diff
- Render the relevant manifests
- Run client-side validation
- Use server-side dry run where appropriate
- Confirm the commit has been pushed
- Understand pruning and dependency behaviour
- Prepare a rollback plan for consequential changes
Do not expose:
- Git credentials
- Deploy keys
- Personal access tokens
- SOPS private keys
- Kubernetes Secrets
- Private repository URLs
- Internal hostnames and addresses
Related entries#
- Kubernetes Cluster Health Checks
- Kubernetes Workload Troubleshooting
- Kustomize Render Validation
- SOPS and age Reference
- HelmRelease Troubleshooting
- Flux Controller Health Checks
- Git Branch and Revision Checks