⚙️ Silent Deploy Freeze

Flux source-controller Fails to Fetch Helm Repository Index

Flux source-controller Helm index fetch failure occurs when the source-controller pod cannot retrieve index.yaml from a Helm repository, so the HelmRepository resource never reaches a ready state. Every HelmRelease that depends on that repository stops receiving chart updates. The cluster keeps running the version it already has, so nothing appears broken while deployments quietly stop being delivered.

TL;DRsource-controller reconciles HelmRepository resources by fetching index.yaml on an interval. When that fetch fails, the repository is marked not-ready and every dependent HelmRelease stalls. Running workloads are unaffected, which is exactly the problem: GitOps reports success on the Git side while chart delivery has stopped. The error text tells you which layer failed, and the progression between error types tells you whether the registry is down or the network path is.

What does the Flux source-controller Helm index error look like?

source-controller logs structured JSON. The useful field is error, and it nests three levels deep: the reconcile failed because the index fetch failed because the underlying HTTP request failed.

{"level":"error","msg":"Reconciler error","controller":"helmrepository",
   "controllerKind":"HelmRepository",
   "HelmRepository":{"name":"charts-internal","namespace":"flux"},
   "error":"failed to fetch Helm repository index: failed to cache index to
    temporary file: Get \"https://charts.internal.example:443/artifactory/
    helm-virtual-all/index.yaml\": dial tcp 10.20.30.40:443: connect: connection refused"}
  

The same reconcile loop produces different tail errors as conditions change. A real twelve-minute window progressed through all three:

Error tail What it means
connect: connection refused Nothing is listening. Registry down, or the service or endpoint is gone.
dial tcp ...: i/o timeout Packets leaving, nothing coming back. Network policy, firewall, or an overloaded host.
503 The registry answered. It is up but unhealthy or rate limiting.

That progression is diagnostic on its own. Refused to timeout to 503 is the signature of a registry restarting and coming back under load, not a misconfiguration on the cluster side. A configuration error produces the same error text every time.

Not to be confused with A HelmChart fetch failure is a different resource. If the HelmRepository is ready but the chart itself will not resolve, the index was retrieved successfully and the problem is a missing chart version or a semver constraint that matches nothing. Check which resource is not-ready before assuming a network cause.

Why does source-controller fail to fetch the index?

source-controller runs one reconcile per HelmRepository per spec.interval. Each reconcile fetches index.yaml, caches it to a temp file, and writes an artifact other controllers consume. Failure at any step marks the resource not-ready and schedules a retry.

The realistic causes, roughly in order of frequency:

  1. The registry is genuinely down or restarting. Self-hosted Artifactory, Nexus, and ChartMuseum instances get patched, and a rolling restart produces exactly this window.
  2. Network path broken between cluster and registry. A NetworkPolicy change, a firewall rule, or an egress gateway failure. Common when the registry lives outside the cluster on a private network.
  3. Credentials expired. spec.secretRef points at a secret with a rotated token. This produces 401 or 403 rather than a dial error.
  4. TLS trust failure. A private CA whose bundle is not mounted, or a certificate that expired. Produces x509 errors in the tail.
  5. DNS resolution failure. Registry hostname no longer resolves from inside the cluster, often after a CoreDNS or upstream resolver change.

The tail of the error string identifies which one you have. Read to the end of the line.

What breaks while the index fetch is failing?

Nothing that a dashboard reports.

Pods keep running. Services keep serving. Git commits keep landing and kustomize-controller keeps reporting success on everything that is not chart-based. The failure is confined to delivery of new chart versions:

  • HelmRelease upgrades stall. A merged change to a chart version is never applied.
  • New HelmRelease resources never install, because their source is not ready.
  • Drift correction stops for chart-managed workloads. Manual changes made to those resources are no longer reverted.
  • The deploy pipeline reports success. CI merged, Flux synced Git, and the chart layer failed downstream of both.

The gap between “deployed” and “actually running” opens silently and stays open until someone notices a version mismatch, often during an unrelated investigation.

How do I fix a Flux Helm repository index fetch failure?

1. Confirm which resource is failing

flux get sources helm --all-namespaces
  kubectl -n flux describe helmrepository charts-internal
  

The Ready condition carries the same error text as the controller log, and the Last Transition Time tells you how long delivery has been stopped.

2. Read the tail of the error

Match the tail to a cause before touching anything:

connection refused  → registry process down or endpoint missing
  i/o timeout         → network path blocked
  503 / 502           → registry up but unhealthy
  401 / 403           → credentials rejected
  x509                → TLS trust or expiry
  no such host        → DNS
  

3. Test the fetch from inside the cluster

Namespace and network policy both matter, so test from a pod in the same namespace as source-controller, not from your laptop:

kubectl -n flux run netcheck --rm -it --image=curlimages/curl --restart=Never -- \
    curl -sS -o /dev/null -w '%{http_code}\n' \
    https://charts.internal.example/artifactory/helm-virtual-all/index.yaml
  

A different result from inside the cluster than from outside points at NetworkPolicy or egress rather than the registry.

4. Check credentials and TLS if the fetch is reaching the registry

kubectl -n flux get helmrepository charts-internal -o jsonpath='{.spec.secretRef.name}'
  kubectl -n flux get secret <name> -o jsonpath='{.data}' | head -c 200
  

For a private CA, confirm spec.certSecretRef is set and the CA bundle in that secret has not expired.

5. Force a reconcile once the cause is cleared

flux reconcile source helm charts-internal -n flux
  flux get sources helm -n flux
  

Without this the resource waits out the remainder of spec.interval, which can be fifteen minutes or more, and it will look like the fix did not work.

6. Make the next one visible

The reason this class of failure runs long is that no alert exists for a not-ready source. If you are alerting, alert on HelmRepository ready-condition age rather than on pod health, because the pod is fine throughout. If you would rather not add another rule to maintain, this is precisely the shape of failure a baseline catches on its own.

How does Dstl8 detect this?

source-controller is one service among hundreds in a cluster, and a reconcile error is not a crash, a restart, or a latency spike. No threshold catches it. Dstl8 baselines each service against its own history, so a burst of reconcile errors from a controller that normally emits none surfaces as an incident, correlated across the namespace and service that produced it.

Real Detection
Powered by CONTROLTHEORY
Incident Resolved PLATFORM

source-controller fails to fetch Helm repository index

source-controller in the flux namespace unable to fetch Helm repository index from the private chart registry, causing reconciliation failures for HelmRepository resources. Errors progressed from connection refused to i/o timeout to 503 across the window. 100% error rate. No alert rule configured.

Started
17:45
Aug 19
Span
15m
Severity
Major
Events
2

The incident correlates the namespace and the service, cites the specific error progression rather than a generic failure count, and closes on its own when reconciles succeed again. The error-type progression is preserved in the narrative, which is the part that tells you the registry was restarting rather than misconfigured.

Frequently asked questions

Why does Flux source-controller fail to fetch a Helm repository index?

The source-controller pod could not retrieve index.yaml from the Helm repository. Common causes are the registry being down or restarting, a blocked network path from a NetworkPolicy or firewall, expired credentials in the referenced secret, a TLS trust or certificate expiry problem, or DNS resolution failure. The tail of the error string identifies which one applies.

What happens to HelmReleases when a HelmRepository is not ready?

Every HelmRelease depending on that repository stops receiving chart updates. Running workloads continue unaffected, new releases never install, and drift correction stops for chart-managed resources. The deploy pipeline still reports success because Git synced correctly, so the failure is silent.

How do I force Flux to retry a failed Helm repository fetch?

Run flux reconcile source helm <name> -n <namespace>. Without forcing it, the controller waits out the remainder of spec.interval, which can be fifteen minutes or more, and it will appear that the fix did not work.

Related patterns

References

Catch stalled delivery before the version drift does.

Dstl8 baselines every controller and service in your cluster and surfaces error patterns that were not there before. No thresholds, no rules, no prior knowledge of what to look for.

Start Free 14-Day Trial →