GitOps traditionally means “Git is the source of truth.” Flux’s OCI artifact support challenges that assumption in an interesting way: instead of pointing Flux at a Git repository, you can point it at an OCI image stored in a container registry. Your Kubernetes configuration is packaged, versioned, and distributed like a container image.
This is more than a deployment detail. OCI artifacts enable use cases that Git-based GitOps handles awkwardly: supply chain security with image signing, air-gapped deployments, centralized artifact management across many clusters, and audit trails that work with existing container security tooling.
What OCI Artifacts Are
OCI (Open Container Initiative) defines a standard for container images. But the OCI spec is more general than most people realize — it’s a standard for distributing arbitrary blobs of data in a layered format, with a standardized manifest format. The same registries (Harbor, Artifactory, ECR, GHCR) that store container images can store Helm charts, WASM modules, and Kubernetes configuration.
Flux supports two types of OCI artifacts:
- Helm charts stored in OCI registries: Instead of traditional Helm chart repositories, charts packaged and pushed to an OCI registry
- Flux sources as OCI artifacts: Kubernetes YAML, Kustomizations, and other resources packaged as OCI artifacts and referenced by Flux
Why OCI Over Git for Infrastructure
The traditional approach: Flux watches a Git repository and applies changes when it detects commits. This works, but has friction points:
Signature verification: Git commit signatures exist but are rarely enforced in CI/CD workflows. OCI images can be signed with Cosign, and Flux can verify those signatures before applying.
Distribution and caching: A Git repository requires network access to a Git server. OCI artifacts work with the existing container registry infrastructure your cluster already has.
Air-gapped environments: In air-gapped networks (government, regulated industries), you can mirror OCI registries more easily than maintaining Git mirrors. Zarf specifically uses OCI artifacts for air-gap package distribution.
Versioning and rollbacks: OCI artifacts are immutable by digest. You can reference config:v1.2.3 and know exactly what you’ll get, with full audit trail through the registry.
Separation of concerns: The team that builds the Helm chart releases it to a registry. The team that operates the cluster references the versioned artifact. Clean boundaries, no direct repo access required.
Publishing Flux Sources as OCI Artifacts
# Install the Flux CLI
curl -s https://fluxcd.io/install.sh | sudo bash
# Package your cluster configuration as an OCI artifact
flux push artifact oci://ghcr.io/myorg/cluster-config:$(git rev-parse --short HEAD) \
--path="./clusters/production" \
--source="$(git config --get remote.origin.url)" \
--revision="$(git rev-parse HEAD)"
# Tag it as 'latest' or a version
flux tag artifact oci://ghcr.io/myorg/cluster-config:$(git rev-parse --short HEAD) \
--tag latest
In a CI pipeline:
# .github/workflows/publish-config.yaml
name: Publish Cluster Config
on:
push:
branches: [main]
paths:
- 'clusters/**'
permissions:
contents: read
packages: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Flux CLI
uses: fluxcd/flux2/action@main
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: $
password: $
- name: Sign artifacts with Cosign
uses: sigstore/cosign-installer@v3
- name: Publish and sign
run: |
VERSION=$(git rev-parse --short HEAD)
flux push artifact \
oci://ghcr.io/$/cluster-config:${VERSION} \
--path="./clusters/production" \
--source="$" \
--revision="${GITHUB_SHA}"
# Sign the artifact
cosign sign --yes \
ghcr.io/$/cluster-config:${VERSION}
# Tag as latest
flux tag artifact \
oci://ghcr.io/$/cluster-config:${VERSION} \
--tag latest
Configuring Flux to Use OCI Sources
# Point Flux at an OCI artifact instead of a Git repository
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
name: cluster-config
namespace: flux-system
spec:
interval: 5m
url: oci://ghcr.io/myorg/cluster-config
ref:
tag: latest
# Verify artifact signature before using
verify:
provider: cosign
secretRef:
name: cosign-public-key
---
# Apply the OCI source via Kustomization
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: cluster-config
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: OCIRepository
name: cluster-config
path: ./
prune: true
wait: true
OCI Helm Charts
Helm supports OCI natively since Helm 3.8. Instead of helm repo add, you pull charts directly:
# Pull a chart from OCI (no helm repo add needed)
helm pull oci://ghcr.io/myorg/charts/myapp --version 1.2.3
# Install directly from OCI
helm install myapp oci://ghcr.io/myorg/charts/myapp \
--version 1.2.3 \
--namespace production \
--values values.yaml
In Flux, reference OCI Helm charts:
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: myorg-charts
namespace: flux-system
spec:
type: oci # OCI registry type
interval: 5m
url: oci://ghcr.io/myorg/charts
secretRef:
name: ghcr-credentials
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: myapp
namespace: production
spec:
interval: 10m
chart:
spec:
chart: myapp
version: "1.2.3"
sourceRef:
kind: HelmRepository
name: myorg-charts
namespace: flux-system
values:
replicaCount: 3
ingress:
enabled: true
Supply Chain Security with Cosign
The combination of OCI artifacts and Cosign signatures creates a verifiable supply chain:
# Generate signing keys (or use keyless signing with Sigstore)
cosign generate-key-pair
# The private key signs, public key is stored in the cluster
kubectl create secret generic cosign-public-key \
--namespace flux-system \
--from-file=cosign.pub=cosign.pub
Flux verifies the signature before applying:
spec:
verify:
provider: cosign
secretRef:
name: cosign-public-key
# Flux will reject artifacts that don't have a valid signature
For keyless signing (Sigstore OIDC-based, no key management):
spec:
verify:
provider: cosign
# Identity from the OIDC provider
matchOIDCIdentity:
- issuer: "https://token.actions.githubusercontent.com"
subject: "repo:myorg/myrepo:ref:refs/heads/main"
This verifies that the artifact was published by a specific GitHub Actions workflow from a specific repository and branch. It’s a strong guarantee without managing signing keys.
Using OCI with Zarf for Air-Gap
Zarf uses OCI artifacts as its packaging format for air-gapped deployments. Flux and Zarf work together:
# zarf.yaml - package Flux sources for air-gap deployment
kind: ZarfPackageConfig
metadata:
name: my-cluster-config
version: "1.0.0"
components:
- name: flux-sources
required: true
images:
- ghcr.io/myorg/cluster-config:1.0.0
manifests:
- name: flux-oci-source
files:
- manifests/oci-repository.yaml
- manifests/kustomization.yaml
The Zarf package bundles the OCI artifact along with its Flux configuration, making the entire thing transferable to an air-gapped network.
Automating Image Updates
Flux’s Image Automation can update OCI source references when new artifacts are published:
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: cluster-config
namespace: flux-system
spec:
image: ghcr.io/myorg/cluster-config
interval: 5m
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: cluster-config-policy
namespace: flux-system
spec:
imageRepositoryRef:
name: cluster-config
policy:
semver:
range: ">=1.0.0" # Use latest semantic version
# In your OCIRepository, Flux Image Automation will update this tag
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
name: cluster-config
spec:
ref:
tag: 1.2.3 # {"$imagepolicy": "flux-system:cluster-config:tag"}
Conclusion
OCI artifacts represent a maturation of GitOps rather than a replacement. Git remains the source of truth for configuration — but instead of Flux reading directly from Git, CI publishes signed, immutable artifacts to a registry, and Flux applies those artifacts.
The benefits compound: stronger supply chain security through signing, better compatibility with air-gapped deployments, cleaner separation between development and operations teams, and reuse of existing container registry infrastructure and security tooling.
For homelab use, the Git-based approach is simpler and probably the right call. But for production environments with security and compliance requirements — especially regulated industries, government, or defense — OCI artifacts with Cosign signing is the direction GitOps is heading.