Flux CLI plugin for mirroring Helm charts and OCI artifacts across registries
See the codeFlux Mirror is a CLI for mirroring Helm charts, OCI artifacts and container images between registries using a declarative approach.
The intended use case is feeding an internal mirror registry that backs Flux OCIRepository and Kubernetes Deployments, so clusters never reach out to upstream registries at reconcile time.
It also enables migration away from HTTP/S HelmRepository sources: chart
versions are republished as OCI Helm artifacts that HelmRelease consumes
via an OCIRepository in spec.chartRef, dropping the runtime dependency
on upstream chart repositories.
regex → semver → sort → top-N filter. For charts, a semver constraint
plus top-N. Sort by semver, alphabetical, or numerical.text and yaml/json for downstream
tooling, plus a verbose mode that streams every blob and manifest digest
for diagnosing TLS, auth, or push failures.Install the plugin with the Flux CLI:
flux plugin install mirror
Authenticate once against the destination and optionally source registries:
docker login ghcr.io
For private HTTP/S Helm repositories, login with Helm:
helm repo add private https://charts.example.com --username "$USER" --password "$TOKEN"
Write a config file describing what to mirror:
# flux-mirror.yaml
apiVersion: mirror.plugin.fluxcd.io/v1beta1
kind: Config
charts:
- name: external-dns
source: https://kubernetes-sigs.github.io/external-dns/
destination: oci://ghcr.io/my-org/charts
version: "*"
limit: 3
artifacts:
- source: registry.k8s.io/external-dns/external-dns
destination: ghcr.io/my-org/external-dns
selector:
semver: ">=0.15.0"
limit: 3
includeReferrers: true
Run the sync:
flux mirror sync flux-mirror.yaml
You can also read the config from stdin:
flux mirror sync - < flux-mirror.yaml
Preview without writing:
flux mirror sync flux-mirror.yaml --dry-run
Force a resync of drifted tags e.g. latest:
flux mirror sync flux-mirror.yaml --overwrite
See examples/ for more configurations and
docs/sync.md for the full flag reference.
flux mirror sync is designed for unattended runs. The exit code separates
real failures from drift, so a CI gate can react to each independently:
| Code | Meaning |
|---|---|
0 | Clean run, every tag was copied or skipped as expected. |
1 | At least one tag job failed (network error, push rejected, retries exhausted). |
2 | No failures, but at least one tag drifted with overwrite: false (configurable with --drift-exit-code). |
The --no-progress flag suppresses the live spinner so log output stays clean in CI:
flux mirror sync flux-mirror.yaml --no-progress
When the destination registry is known to be immutable, drift can be reported without failing the CI job:
flux mirror sync flux-mirror.yaml --no-progress --drift-exit-code=0
For downstream tooling, emit a structured report:
flux mirror sync flux-mirror.yaml -o json | jq '.report.results[].tags'
The fluxcd/flux-mirror/actions/setup composite action
installs the CLI on Ubuntu, macOS, and Windows runners.
Example workflow:
name: mirror-charts
on:
schedule:
- cron: "0 */6 * * *"
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Flux Mirror CLI
uses: fluxcd/flux-mirror/actions/setup@main
- name: Sync Kubernetes SIGs Charts
run: flux-mirror sync kubernetes-sigs.yaml --no-progress
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
The ghcr.io/fluxcd/flux-mirror image can be used in container-based CI pipelines:
docker run --rm \
-e DOCKER_CONFIG=/.docker \
-v "$PWD/flux-mirror.yaml:/config.yaml:ro" \
-v "$HOME/.docker/config.json:/.docker/config.json:ro" \
ghcr.io/fluxcd/flux-mirror:latest sync /config.yaml --no-progress
To run flux-mirror sync from inside a cluster on a schedule, see the
examples/cronjob.yaml manifest. It bundles a
ConfigMap with the sync config and a CronJob that mounts the
destination registry credentials from a Secret created via
flux create secret oci.
| Command | Description |
|---|---|
flux mirror sync [CONFIG] | Mirror Helm charts and OCI artifacts described by a YAML config. |
flux mirror login | Store configured credentials in the Docker config (or OS keychain). |
flux mirror secret <name> | Create/replace (upsert) a dockerconfigjson Kubernetes Secret with per-host credentials. |
flux mirror keygen | Generate an EdDSA JWK pair for JWK-based registry auth. |
flux mirror version | Print the CLI version. |
flux mirror completion | Generate shell completion for bash, fish, powershell and zsh. |
Run flux mirror <command> --help for the full flag list.
hosts,
artifacts, and charts entries, selector pipeline, overwrite semantics,
defaults, and the published JSON Schema.-o json / -o yaml.dockerconfigjson Secret.The Flux Mirror project is Apache 2.0 licensed and accepts contributions via GitHub pull requests.
Go
97.6%
Makefile
1.4%
Flux CLI plugin for mirroring Helm charts and OCI artifacts across registries
See the codeFlux Mirror is a CLI for mirroring Helm charts, OCI artifacts and container images between registries using a declarative approach.
The intended use case is feeding an internal mirror registry that backs Flux OCIRepository and Kubernetes Deployments, so clusters never reach out to upstream registries at reconcile time.
It also enables migration away from HTTP/S HelmRepository sources: chart
versions are republished as OCI Helm artifacts that HelmRelease consumes
via an OCIRepository in spec.chartRef, dropping the runtime dependency
on upstream chart repositories.
regex → semver → sort → top-N filter. For charts, a semver constraint
plus top-N. Sort by semver, alphabetical, or numerical.text and yaml/json for downstream
tooling, plus a verbose mode that streams every blob and manifest digest
for diagnosing TLS, auth, or push failures.Install the plugin with the Flux CLI:
flux plugin install mirror
Authenticate once against the destination and optionally source registries:
docker login ghcr.io
For private HTTP/S Helm repositories, login with Helm:
helm repo add private https://charts.example.com --username "$USER" --password "$TOKEN"
Write a config file describing what to mirror:
# flux-mirror.yaml
apiVersion: mirror.plugin.fluxcd.io/v1beta1
kind: Config
charts:
- name: external-dns
source: https://kubernetes-sigs.github.io/external-dns/
destination: oci://ghcr.io/my-org/charts
version: "*"
limit: 3
artifacts:
- source: registry.k8s.io/external-dns/external-dns
destination: ghcr.io/my-org/external-dns
selector:
semver: ">=0.15.0"
limit: 3
includeReferrers: true
Run the sync:
flux mirror sync flux-mirror.yaml
You can also read the config from stdin:
flux mirror sync - < flux-mirror.yaml
Preview without writing:
flux mirror sync flux-mirror.yaml --dry-run
Force a resync of drifted tags e.g. latest:
flux mirror sync flux-mirror.yaml --overwrite
See examples/ for more configurations and
docs/sync.md for the full flag reference.
flux mirror sync is designed for unattended runs. The exit code separates
real failures from drift, so a CI gate can react to each independently:
| Code | Meaning |
|---|---|
0 | Clean run, every tag was copied or skipped as expected. |
1 | At least one tag job failed (network error, push rejected, retries exhausted). |
2 | No failures, but at least one tag drifted with overwrite: false (configurable with --drift-exit-code). |
The --no-progress flag suppresses the live spinner so log output stays clean in CI:
flux mirror sync flux-mirror.yaml --no-progress
When the destination registry is known to be immutable, drift can be reported without failing the CI job:
flux mirror sync flux-mirror.yaml --no-progress --drift-exit-code=0
For downstream tooling, emit a structured report:
flux mirror sync flux-mirror.yaml -o json | jq '.report.results[].tags'
The fluxcd/flux-mirror/actions/setup composite action
installs the CLI on Ubuntu, macOS, and Windows runners.
Example workflow:
name: mirror-charts
on:
schedule:
- cron: "0 */6 * * *"
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Flux Mirror CLI
uses: fluxcd/flux-mirror/actions/setup@main
- name: Sync Kubernetes SIGs Charts
run: flux-mirror sync kubernetes-sigs.yaml --no-progress
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
The ghcr.io/fluxcd/flux-mirror image can be used in container-based CI pipelines:
docker run --rm \
-e DOCKER_CONFIG=/.docker \
-v "$PWD/flux-mirror.yaml:/config.yaml:ro" \
-v "$HOME/.docker/config.json:/.docker/config.json:ro" \
ghcr.io/fluxcd/flux-mirror:latest sync /config.yaml --no-progress
To run flux-mirror sync from inside a cluster on a schedule, see the
examples/cronjob.yaml manifest. It bundles a
ConfigMap with the sync config and a CronJob that mounts the
destination registry credentials from a Secret created via
flux create secret oci.
| Command | Description |
|---|---|
flux mirror sync [CONFIG] | Mirror Helm charts and OCI artifacts described by a YAML config. |
flux mirror login | Store configured credentials in the Docker config (or OS keychain). |
flux mirror secret <name> | Create/replace (upsert) a dockerconfigjson Kubernetes Secret with per-host credentials. |
flux mirror keygen | Generate an EdDSA JWK pair for JWK-based registry auth. |
flux mirror version | Print the CLI version. |
flux mirror completion | Generate shell completion for bash, fish, powershell and zsh. |
Run flux mirror <command> --help for the full flag list.
hosts,
artifacts, and charts entries, selector pipeline, overwrite semantics,
defaults, and the published JSON Schema.-o json / -o yaml.dockerconfigjson Secret.The Flux Mirror project is Apache 2.0 licensed and accepts contributions via GitHub pull requests.
Go
97.6%
Makefile
1.4%