Skip to content

Latest commit

Β 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

OCI Artifact Distribution with FluxCD

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.

License: MIT Kubernetes FluxCD OCI

πŸ”­ Overview

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.

πŸ—οΈ Architecture

OCI Artifact Distribution with FluxCD architecture

🧩 Components

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

🎯 Objective

This playground demonstrates how to:

  • package Kubernetes configuration as an OCI artifact;
  • publish immutable revisions and a movable latest tag;
  • consume an OCI artifact with FluxCD;
  • reconcile resources without using a GitRepository source;
  • inspect the deployed artifact revision and digest;
  • observe automatic reconciliation after a new artifact is published.

πŸ“‹ Prerequisites

  • a Kubernetes cluster;
  • kubectl configured 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 --version

🐳 Start a local cluster with Docker Desktop

Enable 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 nodes

If the docker-desktop context does not exist, confirm that Kubernetes is enabled in Docker Desktop and that its Kubernetes status is running before continuing.

βš™οΈ Installation

Install the Flux controllers in the current cluster:

flux check --pre
flux install
flux check

This local installation is sufficient for the playground. It does not bootstrap Flux against a Git repository.

πŸ“¦ Publish the OCI artifact

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-stdin

The token needs package write access for manual publication.

Publish the manifests:

./scripts/publish.sh

By 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.sh

Make 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.

πŸš€ Quick Start

Apply the Flux source and reconciliation objects:

kubectl apply -f clusters/demo/all.yaml

Force an immediate pull and reconciliation:

flux reconcile source oci oci-demo --with-source
flux reconcile kustomization oci-demo

βœ… Verification

Inspect the OCI source:

flux get sources oci
kubectl -n flux-system describe ocirepository oci-demo

Inspect the reconciliation:

flux get kustomizations
kubectl -n flux-system describe kustomization oci-demo

Verify the deployed workload:

kubectl -n oci-demo get all
kubectl -n oci-demo rollout status deployment/oci-demo

Access the demo application:

kubectl -n oci-demo port-forward service/oci-demo 8080:80

Open http://localhost:8080 or run:

curl http://localhost:8080

The response should contain:

Deployed from an OCI artifact

πŸ”„ Testing automatic reconciliation

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:8080

πŸ” Inspecting the artifact

Using Flux:

flux pull artifact \
  oci://ghcr.io/openmind-systems-lab/oci-artifact-distribution-playground/manifests:latest \
  --output ./pulled-artifact

Using ORAS:

oras manifest fetch \
  ghcr.io/openmind-systems-lab/oci-artifact-distribution-playground/manifests:latest \
  --pretty

The artifact generated by Flux uses dedicated configuration and content media types for Flux OCI artifacts.

πŸ§ͺ Validation

Run local static checks:

./scripts/validate.sh

Run the end-to-end smoke test after deployment:

./scripts/smoke-test.sh

πŸ” Private registry authentication

Create 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-auth

Do not commit registry credentials to Git.

πŸŽ“ What You Will Learn

  • OCI registries can distribute arbitrary artifacts, not only images.
  • An OCI tag is movable, while a digest identifies immutable content.
  • Flux can use an OCIRepository wherever 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.

🧹 Cleanup

Remove the demo resources:

./scripts/cleanup.sh

Remove Flux from the cluster if it was installed only for this playground:

flux uninstall --silent

Deleting Kubernetes resources does not delete packages already published in GHCR.

πŸ—‚οΈ Repository Structure

.
β”œβ”€β”€ 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

πŸ“š References

πŸ› About OpenMind Systems Lab

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

About

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.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages