Describe the bug
OCIRepository resources targeting public charts on ghcr.io fail to refresh their tag digest after the first successful pull. The reconciler ends up making a request to a non-public GitHub URL (github.com/-/v2/packages/container/package/...) that returns 404, leaving the resource stuck in Ready=False indefinitely.
The image is publicly accessible (verified via curl with an anonymous token exchange), and the digest matches what is pinned in the resource.
Steps to reproduce
-
Apply the following on a cluster with source-controller v1.8.4 (Flux v2.8.7):
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: dragonfly-operator
namespace: flux-system
spec:
interval: 10m
url: oci://ghcr.io/dragonflydb/dragonfly-operator/helm/dragonfly-operator
timeout: 20m
ref:
tag: v1.5.0
-
Wait for the next reconcile cycle (≥ 10m).
-
Observe the controller logs and the resource status.
Actual behavior
OCIRepository reports Ready=False:
failed to determine artifact digest:
GET https://github.com/-/v2/packages/container/package/dragonflydb%2Fdragonfly-operator%2Fhelm%2Fdragonfly-operator%2Fmanifests%2Fv1.5.0:
unexpected status code 404 Not Found: Not Found
Note the URL is github.com/-/v2/packages/container/package/..., not the OCI distribution API endpoint ghcr.io/v2/.... This is an internal GitHub redirect target that is not a public OCI endpoint.
Reproducible with several public ghcr.io charts (e.g. dragonflydb/dragonfly-operator/helm/dragonfly-operator, weaveworks/charts/weave-gitops). Both produce the exact same error pattern.
Expected behavior
The reconciler should authenticate anonymously against ghcr.io (token endpoint ghcr.io/token with the right scope) and successfully retrieve the manifest, the same way docker pull / crane manifest / curl + Bearer do without credentials.
Evidence the image is reachable
Without auth (current source-controller behaviour):
curl -sI https://ghcr.io/v2/dragonflydb/dragonfly-operator/helm/dragonfly-operator/manifests/v1.5.0
# HTTP/2 401
# www-authenticate: Bearer realm="https://ghcr.io/token",
# service="ghcr.io",
# scope="repository:dragonflydb/dragonfly-operator/helm/dragonfly-operator:pull"
With anonymous token exchange:
TOKEN=$(curl -s 'https://ghcr.io/token?scope=repository:dragonflydb/dragonfly-operator/helm/dragonfly-operator:pull' \
| jq -r .token)
curl -sI -H "Authorization: Bearer $TOKEN" \
-H "Accept: application/vnd.oci.image.manifest.v1+json" \
https://ghcr.io/v2/dragonflydb/dragonfly-operator/helm/dragonfly-operator/manifests/v1.5.0
# HTTP/2 200
# docker-content-digest: sha256:dd0656cdc1dc461e73336c88db5b664446621cdbcc65f0c44a184a4d34fe7c4c
So the registry path is correct and the manifest exists. The 401 → token → retry flow is what fails inside source-controller.
Workaround
Migrating the same chart to HelmRepository{type: oci} + HelmRelease.chart.spec works without any change in credentials or network setup, suggesting the code path used by HelmRepository{type: oci} handles the auth challenge correctly while OCIRepository does not.
Environment
- Flux version: v2.8.7
- source-controller: v1.8.4 (from
gotk-components.yaml)
- Install method: bootstrap via
flux install
- Kubernetes: v1.35 on Talos Linux v1.12.4
- Registry: ghcr.io, public repos, no credentials configured
Related closed issues
Describe the bug
OCIRepositoryresources targeting public charts onghcr.iofail to refresh their tag digest after the first successful pull. The reconciler ends up making a request to a non-public GitHub URL (github.com/-/v2/packages/container/package/...) that returns 404, leaving the resource stuck inReady=Falseindefinitely.The image is publicly accessible (verified via
curlwith an anonymous token exchange), and the digest matches what is pinned in the resource.Steps to reproduce
Apply the following on a cluster with
source-controllerv1.8.4 (Flux v2.8.7):Wait for the next reconcile cycle (≥ 10m).
Observe the controller logs and the resource status.
Actual behavior
OCIRepositoryreportsReady=False:Note the URL is
github.com/-/v2/packages/container/package/..., not the OCI distribution API endpointghcr.io/v2/.... This is an internal GitHub redirect target that is not a public OCI endpoint.Reproducible with several public ghcr.io charts (e.g.
dragonflydb/dragonfly-operator/helm/dragonfly-operator,weaveworks/charts/weave-gitops). Both produce the exact same error pattern.Expected behavior
The reconciler should authenticate anonymously against
ghcr.io(token endpointghcr.io/tokenwith the right scope) and successfully retrieve the manifest, the same waydocker pull/crane manifest/curl + Bearerdo without credentials.Evidence the image is reachable
Without auth (current source-controller behaviour):
With anonymous token exchange:
So the registry path is correct and the manifest exists. The 401 → token → retry flow is what fails inside
source-controller.Workaround
Migrating the same chart to
HelmRepository{type: oci}+HelmRelease.chart.specworks without any change in credentials or network setup, suggesting the code path used byHelmRepository{type: oci}handles the auth challenge correctly whileOCIRepositorydoes not.Environment
gotk-components.yaml)flux installRelated closed issues
OCIRepositoriescan no longer specify tag@digest #1471 — same family (regression in tag@digest) — appears to be a different root cause but worth referencing.