K3s can install in an air-gapped environment, but you’ll need to manually fetch and transfer all its dependencies.
Let’s get K3s running on a cluster where no node has internet access. The key is to download everything needed on a machine with internet access, then move it all to your air-gapped network.
Here’s a typical K3s installation on a single master node, followed by adding a worker.
Master Node Setup
First, on a machine with internet:
# Define K3s version and architecture
K3S_VERSION="v1.28.5+k3s1"
ARCH="amd64"
# Download K3s binary
curl -sfL "https://github.com/k3s-io/k3s/releases/download/${K3S_VERSION}/k3s-${ARCH}" -o k3s
# Download Kubernetes manifests for CNI and CoreDNS
# These are embedded in the k3s binary, but we need them separate for air-gapped.
# The exact URLs can be found by inspecting the k3s binary or release notes.
# For v1.28.5+k3s1, these are typically:
curl -sfL "https://github.com/k3s-io/k3s/releases/download/${K3S_VERSION}/k3s-airgap-images-${ARCH}.tar" -o k3s-airgap-images-${ARCH}.tar
curl -sfL "https://github.com/k3s-io/k3s/releases/download/${K3S_VERSION}/k3s-install.sh" -o k3s-install.sh
# Make executables
chmod +x k3s k3s-install.sh
Now, transfer k3s, k3s-install.sh, and k3s-airgap-images-${ARCH}.tar to your air-gapped master node. You can use scp, a USB drive, or any other method.
On the air-gapped master node:
# Make executables if not already done
chmod +x k3s k3s-install.sh
# Install K3s, pointing to the local image tarball
INSTALL_K3S_EXEC="server" \
INSTALL_K3S_BIN_DIR="/usr/local/bin" \
K3S_IMAGE_TARBALL="/path/to/your/k3s-airgap-images-amd64.tar" \
sh k3s-install.sh
This command installs K3s as a server (master). The K3S_IMAGE_TARBALL variable tells the installer to load images from your local tar file instead of trying to pull them from Docker Hub.
Worker Node Setup
On a machine with internet access, download the k3s binary and the k3s-install.sh script again. You don’t need the k3s-airgap-images.tar for workers if the master already has them loaded.
# Define K3s version and architecture
K3S_VERSION="v1.28.5+k3s1"
ARCH="amd64"
# Download K3s binary
curl -sfL "https://github.com/k3s-io/k3s/releases/download/${K3S_VERSION}/k3s-${ARCH}" -o k3s
# Download K3s install script
curl -sfL "https://github.com/k3s-io/k3s/releases/download/${K3S_VERSION}/k3s-install.sh" -o k3s-install.sh
# Make executables
chmod +x k3s k3s-install.sh
Transfer k3s and k3s-install.sh to your air-gapped worker node.
On the air-gapped worker node:
# Make executables if not already done
chmod +x k3s k3s-install.sh
# Install K3s as an agent, pointing to the master's IP
# Replace <MASTER_NODE_IP> with the actual IP of your K3s master node.
# Replace <NODE_TOKEN> with the token obtained from the master node.
INSTALL_K3S_EXEC="agent" \
INSTALL_K3S_BIN_DIR="/usr/local/bin" \
K3S_URL="https://<MASTER_NODE_IP>:6443" \
K3S_TOKEN="<NODE_TOKEN>" \
sh k3s-install.sh
To get the <NODE_TOKEN>, check the /var/lib/rancher/k3s/server/node-token file on your master node.
The magic of K3s for air-gapped installs is that the k3s-install.sh script, when provided with K3S_IMAGE_TARBALL, will load all necessary container images (like CoreDNS, CNI plugins, and K3s’s own internal images) from that local file. This bypasses the need for the nodes to reach out to external registries like Docker Hub. The K3S_URL and K3S_TOKEN then allow the agent to join the existing cluster managed by the server.
The next challenge you’ll likely face is updating K3s or its components in your air-gapped environment.