Air-gapped installation¶
An air-gapped build is more than installing the malmok-airgap binary. Every
artifact the cluster consumes must cross the boundary deliberately, and the
document must name where each one was placed.
Malmok's air-gap path is the verification matrix's airgap-pair case: two
nodes on real hardware with egress dropped for the nodes and their pods alike,
passing on 0.96.7. Runs before that dropped only the nodes' own traffic. See
the README's verification table for the conditions.
What must cross the boundary¶
Prepare these on a connected staging machine:
- the Linux
malmok-airgapbinary for the operator architecture; - the RKE2 tarball, its checksum file and the upstream RKE2
install.sh; - the RKE2 image archives required by the selected dataplane;
- the Gateway API bundle when Gateway API CRDs are enabled;
- every application image the cluster will run;
- the images the platform pulls, as one file per architecture:
malmok-images_<tag>_linux_<arch>.tar.zstholds every one, built from the same list the binary prints. The bundle is rebuilt only when that list changes. A release whose images are the same as an earlier one's says so in its notes and links that release's bundle with its checksums, so the<tag>in the name can be older than the release you are installing. Put it on each node in/var/lib/rancher/rke2/agent/images/before RKE2 starts, and containerd imports it the way it imports RKE2's own archives. Give it a name that sorts after RKE2's archives, such aszz-malmok-images_<tag>_linux_<arch>.tar.zst: RKE2 imports the directory in name order, and under its own name the bundle goes first, so the pause image arrives late and containerd tries docker.io for it once on the first start. The install still succeeds, but a closed site's firewall logs the attempt. With a VIP the list includes kube-vip's image. Lists and bundles up to 0.96.7 left it out, and an air-gapped build with a VIP stopped at the vip step waiting for it; with one of those, carryghcr.io/kube-vip/kube-vipat the tagmalmok imagesprints now. To check the list rather than carry the bundle,malmok images -f cluster.yamlprints it and answers on a machine with no network;images.txtin the release is the same list for planning before you have anywhere to run the binary; and - the Helm charts for cert-manager, observability or Argo CD when those
components are enabled -- either mirrored, or carried as
.tgzfiles and named withregistry.chartDir.
The release's air-gap Malmok binary embeds Helm and k9s for amd64 or arm64. It does not embed RKE2, Kubernetes images or your application payloads.
No default alert rules or dashboards
The metrics stack installs its default alert rules and dashboards through a
sync job that fetches them from GitHub when it runs. On a closed network it
cannot, so with network.mode: airgap Malmok turns that job off. Metrics
are still collected and stored; the stack simply starts with no alert
rules and no dashboards. Carrying them in is not supported yet. With pods
genuinely cut off on the lab, the job left on exhausted its retries in five
and a half minutes and created nothing.
Stage the nodes¶
Place the RKE2 artifacts in the same directory on each node. The exact list
depends on the RKE2 version and dataplane; malmok preflight reports every
missing filename before the mutating install begins.
The directory normally contains:
/srv/malmok/rke2/
├── install.sh
├── rke2.linux-amd64.tar.gz
├── sha256sum-amd64.txt
├── rke2-images-*.linux-amd64.tar.zst
└── gateway-api-*-standard-install.yaml
Use arm64 artifacts on arm64 nodes. Do not infer the required image set from
this illustrative listing; let preflight evaluate it against the chosen RKE2
version and dataplane.
Describe the offline sources¶
The important part of an air-gapped document looks like this:
metadata:
profile: airgap-ubuntu
network:
mode: airgap
os:
ntpServers:
- 10.20.0.10
kubernetes:
version: v1.36.3+rke2r1
artifactPath: /srv/malmok/rke2
registry:
mode: internal
systemDefaultRegistry: harbor.internal.example
chartRepo: oci://harbor.internal.example/charts
This is a fragment, not a complete cluster.yaml. Add topology, PKI, storage
and platform settings for the site.
A site with no registry of its own does not have to stand one up. Carry the chart archives instead and name the directory holding them:
Malmok reads them next to the document and embeds each one in its HelmChart, so nothing is fetched at install time. PF-710 names the exact files to stage, and names a wrong version separately -- a directory that looks right is the mistake worth catching before the install window.
With registry.mode: embedded, RKE2's own registry mirror is enabled and the
nodes share images peer to peer. Images preloaded from a tarball are pinned and
shared under whatever registry they are tagged for, including one that does not
exist, so a bundle seeded onto one node reaches the rest: staging is per
cluster, not per node. The mirror needs TCP 5001 open between nodes, and a
closed 5001 does not fail -- containerd falls back to the upstream registry,
which on a closed node is a pull that hangs.
A chart mirror and an image mirror are different inputs
systemDefaultRegistry redirects container images. chartRepo tells
Malmok where Helm chart definitions live. Mirroring only one produces an
installation that still attempts egress.
Validate the hand-carried set¶
Run the non-mutating gates before entering the installation window:
malmok plan -f cluster.yaml --validate-only
malmok preflight -f cluster.yaml
malmok plan -f cluster.yaml
Air-gap-specific preflight checks cover the artifact directory, image sources, chart source, NTP reachability and certificate-chain behavior without relying on a public network.
Install and preserve the evidence¶
Keep the input document, release checksums, event stream and handoff report together. They are the evidence needed to reproduce or audit the build after the original installation window has closed.