> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Push Kubernetes Manifest to OCI Registry

> Packaging Kubernetes manifests as OCI artifacts and pushing them to GitHub Container Registry for Flux to fetch and deploy, including authentication and optional signing

In this lesson we'll package plain Kubernetes manifests as OCI artifacts and push them to an OCI-compliant registry so Flux can later pull and deploy them. We use GitHub Container Registry (GHCR, part of GitHub Packages) as the remote OCI registry for the demo.

Before you begin, you’ll need a GitHub Personal Access Token (PAT) with package-related scopes (read and write for packages; delete is optional). If you already created a PAT during Flux installation you can reuse it, or create/edit a new classic token. Store the token securely — GitHub shows it only once.

![A screenshot of the GitHub Personal Access Token settings page showing a token named "flux-demo" with selected scopes like repo, write:packages, and delete:packages. The page notes the token has no expiration date and displays multiple checkbox options for OAuth scopes.](https://mintcdn.com/kodekloud-c4ac6d9a/AyhVWHpoMIYCWiei/images/GitOps-with-FluxCD/Helm-Controller-and-OCI-Registry/Demo-Push-Kubernetes-Manifest-to-OCI-Registry/github-pat-flux-demo-no-expiration.jpg?fit=max\&auto=format\&n=AyhVWHpoMIYCWiei\&q=85\&s=fccbaa6b8edea507ca8209dd242c9164)

Ensure you save the PAT when you create it. GitHub will not show it again after you leave the creation page.

For private GHCR packages, the PAT must include package read and write permissions. Grant the least privileges required and rotate tokens regularly.

## Repo layout and local setup

* Manifests used in this demo live in the Git branch `7-demo` under `7.7.0/manifests`.
* If you encounter a Git "dubious ownership" error while switching branches on a VM (common when running as root), add the repository to Git's safe list:

```bash theme={null}
git checkout 7-demo
# If you see:
# fatal: detected dubious ownership in repository at '/home/batman/bb-app-source'
# add the repo as safe and retry:
git config --global --add safe.directory /home/batman/bb-app-source

# Retry the checkout and move into the version directory:
git checkout 7-demo
cd 7.7.0
```

* To inspect the directory tree (install `tree` if missing):

```bash theme={null}
apt-get update && apt-get install -y tree
tree -a
```

## Authenticate to GHCR

Flux’s `push artifact` command relies on Docker credentials stored in `~/.docker/config.json` (created by `docker login`). Authenticate using your GitHub username and the PAT as the password.

Interactive login:

```bash theme={null}
docker login ghcr.io --username <your-github-username>
# Password: <paste your PAT>
# Login Succeeded
```

Non-interactive (recommended for CI):

```bash theme={null}
echo "$GITHUB_PAT" | docker login ghcr.io --username <your-github-username> --password-stdin
```

After login, `~/.docker/config.json` contains the credentials that Flux will use to push artifacts.

## Push manifests as an OCI artifact with Flux

Use `flux push artifact` to package a directory (or a single file) into a tarball and publish it to an OCI repository. In this example we push the `7.7.0/manifests` directory to GHCR, tag the artifact with `7.7.0-<short-git-sha>`, and record the Git source and revision metadata.

Example command:

```bash theme={null}
flux push artifact oci://ghcr.io/<your-github-username>/bb-app:7.7.0-$(git rev-parse --short HEAD) \
  --path="./7.7.0/manifests" \
  --source="$(git config --get remote.origin.url)" \
  --revision="7.7.0/$(git rev-parse --short HEAD)"
```

Tip: capture the digest (artifact reference with sha256) for future use:

```bash theme={null}
digest_url=$(flux push artifact \
  oci://ghcr.io/<your-github-username>/bb-app:7.7.0-$(git rev-parse --short HEAD) \
  --path="./7.7.0/manifests" \
  --source="$(git config --get remote.origin.url)" \
  --revision="7.7.0/$(git rev-parse HEAD)")

# digest_url contains the pushed artifact reference with its sha256 digest
echo "$digest_url"
```

Sample successful output (digest will differ):

```bash theme={null}
✓ artifact successfully pushed to ghcr.io/<your-github-username>/bb-app@sha256:379024c535859545876ae260f5a2367f8b34a758528fc6074d58c8ec9fbbf28c
```

### Flags and metadata explained

| Flag | Purpose | Example |
| - | - | - |
| `oci://ghcr.io/<owner>/bb-app:<tag>` | OCI repository URL and artifact tag | `oci://ghcr.io/jane/bb-app:7.7.0-abc123` |
| `--path` | Local directory or file to include in the artifact | `--path="./7.7.0/manifests"` |
| `--source` | Source URL metadata (typically the Git remote URL) | `--source="$(git config --get remote.origin.url)"` |
| `--revision` | Human-readable revision metadata (branch plus commit SHA) | `--revision="7.7.0/$(git rev-parse --short HEAD)"` |

Optional: sign the artifact with cosign for provenance and verification. See Cosign docs for signing and verification workflows.

## Verify the package on GitHub

Refresh your repository's Packages page on GitHub after the push. The package should appear (often marked private). The pulling client (Flux or Docker) must authenticate to GHCR with a token that has package read access.

![A GitHub user profile page with a large circular avatar showing a smiling man. The page's Packages tab is open, listing one private package named "bb-app."](https://mintcdn.com/kodekloud-c4ac6d9a/AyhVWHpoMIYCWiei/images/GitOps-with-FluxCD/Helm-Controller-and-OCI-Registry/Demo-Push-Kubernetes-Manifest-to-OCI-Registry/github-user-packages-bb-app.jpg?fit=max\&auto=format\&n=AyhVWHpoMIYCWiei\&q=85\&s=a102656cb21a89150984e12f1ef5547a)

You can also pull the artifact manually with Docker (authentication required for private packages):

```bash theme={null}
docker pull ghcr.io/<your-github-username>/bb-app:7.7.0-<short-sha>
```

## Next steps

* Create a Kubernetes secret containing credentials for GHCR and configure Flux to use it when fetching private OCI artifacts and Helm charts.
* Consider signing artifacts with cosign and configuring Flux to verify signatures during reconciliation.
* Read more:
  * Flux push artifact docs: [https://fluxcd.io/docs/cmd/flux/push\_artifact/](https://fluxcd.io/docs/cmd/flux/push_artifact/)
  * GitHub Packages / GHCR: [https://docs.github.com/en/packages](https://docs.github.com/en/packages)
  * Cosign (sigstore): [https://docs.sigstore.dev/cosign/](https://docs.sigstore.dev/cosign/)

That’s all for this lesson — you’ve packaged Kubernetes manifests as an OCI artifact and pushed them to GHCR so Flux can later fetch and apply them.

> **[Watch Video](https://learn.kodekloud.com/user/courses/gitops-with-fluxcd/module/205ec7c7-4cb6-4ecb-9bb5-fa50419f1e68/lesson/13765f33-6b31-4a36-bb10-d07ab3bfa621)**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.