CLI tool
zarf-dev/zarf avatar
zarf-dev/zarf

Zarf: packaging Kubernetes software for airgapped clusters

The Airgap Native Package Manager for Kubernetes. If you want to remove it, you can still use your Helm charts to deploy your software manually.

2,049 stars280 forksGoApache-2.0

At a glance

What is it?
Zarf turns a Kubernetes application and everything it pulls from the internet into one archive you can carry into a disconnected network. It is Apache-2.0 Go software aimed at teams that ship to environments with no registry, no Helm repo and no route out.
Who is it for?
Adopt Zarf if your deployment target has no internet path and you already think in Helm charts or raw manifests, because the package format keeps those artifacts usable after you stop using Zarf. Do not adopt it if your clusters can reach a registry and a chart repository, since the packaging step then buys you nothing.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem Zarf targets: Kubernetes delivery without a network path

A normal Kubernetes deploy assumes the cluster can reach things. The kubelet pulls images from a registry. Helm fetches a chart from a repository. An operator may need a Git repository, a database image, or a set of CRDs that live on GitHub. In a connected environment that is fine. In a disconnected one, every one of those fetches fails, and the failure usually shows up as ImagePullBackOff or a timeout in a job that was supposed to seed data.

The README frames Zarf as a way to remove "the complexity of airgap software delivery" for Kubernetes clusters using a declarative packaging strategy. The practical reading: you assemble the parts your application needs while you still have internet access, and Zarf writes them into a single compressed file. That file moves by whatever means the environment allows, and a Zarf command installs from it without connectivity.

The audience is narrow and specific. It is platform or DevSecOps engineers who deploy into edge sites, embedded systems, secure clouds, data centers, or laboratory networks. The README lists exactly those settings. If your clusters have a working egress path, Zarf is solving a problem you do not have, and the extra packaging step is pure overhead.

How a Zarf package is built and what lands in the cluster

The unit of work is a zarf.yaml file. It declares components, and each component carries the resources that component needs: manifests, Helm charts, images, Git repositories, files. The repository ships a zarf.schema.json at the top level, so editors can validate the file as you write it, and examples/ contains working packages for argocd, longhorn, wordpress, podinfo-flux and others.

Building produces one archive. The README calls this a single compressed file that contains the parts of the internet your app needs. Deploying that archive is where the mechanism gets interesting. Zarf can start a cluster from scratch with K3s while fully disconnected, or install into an existing cluster through a kube config. During init it can stand up supporting services inside the cluster: a Docker registry, a Git server based on Gitea, and a K9s dashboard for terminal access.

That in-cluster registry is the piece that makes airgap work tractable. Images that would normally come from Docker Hub are pushed to the local registry, and a mutating webhook rewrites pod image paths and pull secrets so the workloads point at the local copy instead. The same webhook rewrites Flux GitRepository URLs and secret references. You do not edit every manifest by hand. The trade-off is that you are now running a registry and a Git server inside the cluster you are trying to deploy to, which is more moving parts than a plain kubectl apply.

Installing Zarf and running a first package

The README does not carry install commands. It says to follow the instructions at docs.zarf.dev/getting-started, and the documentation site links an installation page at docs.zarf.dev/getting-started/install. The README describes the CLI as a statically compiled binary with zero dependencies to run on any machine, and the project publishes releases on GitHub, so the install path is a downloaded binary rather than a package manager. There is also a setup-zarf GitHub action for workflows.

Once you have the binary, the first real step is initializing a cluster, which is what installs the in-cluster services. The README links a command reference at docs.zarf.dev/commands, and the init flow is the entry point for the registry, the Git server and the webhook. Because the exact flags are not in the README, check the command reference before running anything against a cluster you care about.

The package definition itself is a YAML file. The repository's zarf.schema.json describes its shape, and the README documents components, component imports and component-level OS and architecture filtering. The components key is the important part: each entry is a unit you can include or exclude, and imports let you compose larger packages out of smaller ones. That filtering matters when one package has to serve both amd64 data center nodes and arm64 edge devices.

Building and deploying are separate commands. The README names zarf package publish, zarf package pull and zarf package deploy as the OCI-facing operations, which means a built package can be pushed to an OCI registry and pulled back later. For a genuinely disconnected site, you build, carry the archive, and deploy. What you should see after a successful deploy is your workloads running with images served from the local registry rather than an external one.

Where Zarf gets in the way

The package is a snapshot. When you build, you resolve chart versions and image tags at that moment. If a component pulls a chart by URL without a pinned version, the next build can produce a different archive from identical source, which defeats the declarative promise. Pinning is your job, not Zarf's.

Size is the second constraint. Everything the application needs goes into one file. A package with a handful of images is manageable; a package that bundles a full observability stack plus a database plus a Git server is not something you casually email. The README does not document a size limit, but the single-file design means transfer capacity is a real planning input.

Running the registry and Gitea inside the target cluster adds failure modes that a connected deployment does not have. If the local registry pod is unhealthy, image pulls fail even though the images are sitting in the archive. That is a self-inflicted dependency, and it is the price of not needing an external one.

Finally, Zarf is the wrong tool when the environment is connected. It is also the wrong tool if you only need to move a few images: a straightforward docker save and docker load pipeline does that with less machinery. Zarf earns its place when you need charts, images, Git data, manifests and lifecycle actions resolved together and delivered as one artifact.

Zarf against plain Helm and a manual image push

The honest alternative is what most teams do today: helm template or helm package on a connected machine, docker pull and docker save for each image, then scp the tarballs to the target and load them by hand. This works. It has no in-cluster components and nothing new to learn.

The difference is where the knowledge lives. With the manual approach, the list of images your charts actually reference is in someone's head or in a script that drifts. Zarf derives that list from the package definition and, per the README, ships a command to find images and resources from a Helm chart (zarf dev find-images). The mutating webhook then rewrites the references at deploy time, so the manifests do not have to be edited to point at a local registry.

The second difference is provenance. Zarf can generate an SBOM for the package, publish and pull packages as OCI artifacts, and create and verify signatures with cosign. A shell script that tars up images gives you none of that. If your environment has an audit requirement, that gap is usually the deciding factor.

The README makes one point that matters for the comparison: there is no vendor lock, and if you remove Zarf you can still use your Helm charts to deploy manually. The package format is a wrapper around artifacts you already had, not a replacement for them. That is a meaningful difference from tools that require you to rewrite your deployment definitions in their own dialect.

Maintenance, releases and the licence

The repository is not archived, and the last push was on 2026-08-20. The most recent release is v0.84.0, tagged the same day, with v0.84.0-rc1 earlier that evening and v0.83.0 on 2026-08-07. Release cadence at that point is roughly every two weeks, with release candidates published before the final tag. Version numbers are still in the 0.x range, which is worth noting if you need API stability guarantees across upgrades.

On upgrade cost, the repository layout supports a few observations. The project uses release-please and renovate configuration files at the top level, and the go.mod pins a Go version and carries two replace directives, one for a gojsonschema fork and one holding modernc.org/sqlite at v1.32.0 pending an upstream fix. Those replace directives mean the dependency graph is not purely stock. The repository also vendors its dependencies, so builds do not fetch from the network, which is consistent with the project's own offline posture.

Licensing is Apache-2.0, as stated in the repository and in the SPDX headers on files such as the Makefile. Apache-2.0 includes an explicit patent grant and requires that you preserve notices. It does not impose copyleft obligations on your own code. That is a general description of the licence, not legal advice; if you are redistributing Zarf inside a commercial product, have counsel read the LICENSE file rather than this paragraph.

Editorial conclusion

Adopt Zarf if your deployment target has no internet path and you already think in Helm charts or raw manifests, because the package format keeps those artifacts usable after you stop using Zarf. Do not adopt it if your clusters can reach a registry and a chart repository, since the packaging step then buys you nothing. Before committing, verify three things on your own hardware: that the init package can start the in-cluster registry and Gitea on your node images, that your charts deploy unchanged through the mutating webhook, and that your images fit in one archive at your transfer sizes. The README points at docs.zarf.dev/getting-started for install instructions, and that page is the first thing to read, not this article.

Frequently asked questions

What is Zarf used for?

Zarf packages the parts of the internet a Kubernetes application needs into a single compressed file so it can be deployed into a disconnected environment. It targets airgap and semi-connected settings such as edge, embedded systems, secure clouds and data centers.

What does the Arabic word "zarf" mean?

The README does not discuss the origin or meaning of the name, so there is nothing in the repository to confirm this. Zarf here is the name of the open source Kubernetes packaging tool maintained at github.com/zarf-dev/zarf.

How does Zarf work?

You declare components in a zarf.yaml file, build them into one archive, then deploy that archive. During init Zarf can start an in-cluster Docker registry and a Gitea-based Git server, and a mutating webhook rewrites pod image paths, pull secrets and Flux GitRepository URLs to point at the local copies.

Is "zarf" a real word?

The README does not address the etymology of the name, so the repository cannot settle this. What it does document is the tool itself: a statically compiled Go CLI that packages Kubernetes applications for disconnected environments.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zarf-dev-zarf.svg)](https://hysenlabs.com/projects/zarf-dev-zarf)