A hands-on Proof of Concept showing how Kubernetes manifests can be packaged as an OCI artifact, published to GitHub Container Registry, and reconciled by FluxCD.
This repository is an educational playground. It is not a production-ready delivery platform.
Container registries can distribute more than container images. In this playground, the final Kubernetes manifests are packaged with the Flux CLI and stored in GHCR using OCI media types. Flux running in Kubernetes watches the registry, downloads the artifact, extracts it, and applies the desired state.
The Git repository remains the authoring source, while the OCI registry becomes the deployment distribution source.
| Component | Purpose |
|---|---|
artifact/ |
Content packaged and published as the OCI artifact |
| GHCR | OCI-compatible artifact registry |
Flux OCIRepository |
Pulls and extracts the artifact |
Flux Kustomization |
Reconciles the extracted Kubernetes manifests |
| GitHub Actions | Publishes immutable and latest artifact tags |
| NGINX demo | Verifies that the artifact was deployed successfully |
This playground demonstrates how to:
- package Kubernetes configuration as an OCI artifact;
- publish immutable revisions and a movable
latesttag; - consume an OCI artifact with FluxCD;
- reconcile resources without using a
GitRepositorysource; - inspect the deployed artifact revision and digest;
- observe automatic reconciliation after a new artifact is published.
- a Kubernetes cluster;
kubectlconfigured for the cluster;- the Flux CLI;
- Docker or another registry login helper;
- a GitHub account with permission to publish to GHCR;
- optional: ORAS for low-level artifact inspection.
Check the environment:
kubectl version --client
flux --version
docker --versionEnable Kubernetes once in Docker Desktop > Settings > Kubernetes, then start Docker Desktop and wait for the cluster to be ready:
docker desktop start
docker desktop status
docker desktop kubernetes status
kubectl config use-context docker-desktop
kubectl wait --for=condition=Ready nodes --all --timeout=180s
kubectl get nodesIf the docker-desktop context does not exist, confirm that Kubernetes is enabled
in Docker Desktop and that its Kubernetes status is running before continuing.
Install the Flux controllers in the current cluster:
flux check --pre
flux install
flux checkThis local installation is sufficient for the playground. It does not bootstrap Flux against a Git repository.
Authenticate to GHCR:
export GITHUB_USER="your-github-user"
export GITHUB_TOKEN="your-token"
echo "$GITHUB_TOKEN" | docker login ghcr.io \
--username "$GITHUB_USER" \
--password-stdinThe token needs package write access for manual publication.
Publish the manifests:
./scripts/publish.shBy default, the script publishes to:
oci://ghcr.io/openmind-systems-lab/oci-artifact-distribution-playground/manifests:<git-sha>
It then points the latest tag to the same immutable artifact.
To use a fork or another registry path:
REGISTRY_REPOSITORY=ghcr.io/<owner>/<repository>/manifests \
TAG=v0.1.0 \
./scripts/publish.shMake the GHCR package public so the cluster can pull it anonymously. For a private package, configure a Kubernetes registry secret and reference it from OCIRepository.spec.secretRef.
Apply the Flux source and reconciliation objects:
kubectl apply -f clusters/demo/all.yamlForce an immediate pull and reconciliation:
flux reconcile source oci oci-demo --with-source
flux reconcile kustomization oci-demoInspect the OCI source:
flux get sources oci
kubectl -n flux-system describe ocirepository oci-demoInspect the reconciliation:
flux get kustomizations
kubectl -n flux-system describe kustomization oci-demoVerify the deployed workload:
kubectl -n oci-demo get all
kubectl -n oci-demo rollout status deployment/oci-demoAccess the demo application:
kubectl -n oci-demo port-forward service/oci-demo 8080:80Open http://localhost:8080 or run:
curl http://localhost:8080The response should contain:
Deployed from an OCI artifact
Edit the HTML message in artifact/app/configmap.yaml, commit the change, and push it to main. The GitHub Actions workflow publishes a new immutable artifact and updates the latest tag.
Flux detects the new digest during its next source reconciliation and applies the updated ConfigMap. Because the ConfigMap is mounted as files, NGINX serves the updated content without rebuilding a container image.
Trigger reconciliation manually when testing:
flux reconcile source oci oci-demo
flux reconcile kustomization oci-demo
curl http://localhost:8080Using Flux:
flux pull artifact \
oci://ghcr.io/openmind-systems-lab/oci-artifact-distribution-playground/manifests:latest \
--output ./pulled-artifactUsing ORAS:
oras manifest fetch \
ghcr.io/openmind-systems-lab/oci-artifact-distribution-playground/manifests:latest \
--prettyThe artifact generated by Flux uses dedicated configuration and content media types for Flux OCI artifacts.
Run local static checks:
./scripts/validate.shRun the end-to-end smoke test after deployment:
./scripts/smoke-test.shCreate a Docker registry secret in flux-system:
kubectl -n flux-system create secret docker-registry ghcr-auth \
--docker-server=ghcr.io \
--docker-username="$GITHUB_USER" \
--docker-password="$GITHUB_TOKEN"Then add this field to OCIRepository.spec:
secretRef:
name: ghcr-authDo not commit registry credentials to Git.
- OCI registries can distribute arbitrary artifacts, not only images.
- An OCI tag is movable, while a digest identifies immutable content.
- Flux can use an
OCIRepositorywherever it would normally use a source artifact. - CI can render and publish final deployment manifests before reconciliation.
- Registry-based delivery can separate source authoring from deployment distribution.
Remove the demo resources:
./scripts/cleanup.shRemove Flux from the cluster if it was installed only for this playground:
flux uninstall --silentDeleting Kubernetes resources does not delete packages already published in GHCR.
.
βββ artifact/
β βββ app/ # Kubernetes content published to OCI
βββ clusters/
β βββ demo/ # Flux OCIRepository and Kustomization
βββ scripts/
β βββ publish.sh
β βββ validate.sh
β βββ smoke-test.sh
β βββ cleanup.sh
βββ .github/workflows/
βββ publish-oci-artifact.yaml
- Flux OCI artifacts documentation
- Flux OCIRepository API documentation
- OCI Distribution Specification
- ORAS documentation
- GitHub Container Registry documentation
OpenMind Systems Lab is an independent French non-profit association dedicated to research, experimental development and technical benchmarking in Cloud Native technologies.
Our mission is to produce practical, reproducible and educational Open Source Proofs of Concept covering Kubernetes, Platform Engineering, Distributed Messaging, Infrastructure Security and Artificial Intelligence.
GitHub Organization:
https://github.com/openmind-systems-lab
Made with β€οΈ by OpenMind Systems Lab

