kubeadm Air-Gapped Bootstrapping: Official Guide

K8s Guru
6 min read
kubeadm Air-Gapped Bootstrapping: Official Guide

Introduction

In October 2023, the Kubernetes project published an official guide for bootstrapping air-gapped clusters with kubeadm, providing step-by-step instructions for deploying Kubernetes in environments without direct internet access. This guide addressed a critical gap: while air-gapped bootstrapping had been possible for years, the official documentation made it accessible to teams with strict security requirements or disconnected network architectures.

This mattered because air-gapped deployments represented some of the most challenging Kubernetes use cases: government systems, financial services, industrial control systems, and secure research environments. The official guide provided a standardized, tested approach that teams could follow with confidence, reducing the risk of deployment failures and security vulnerabilities.

Historical note: While kubeadm had supported offline deployments since 2018, the October 2023 guide represented the first comprehensive, officially maintained documentation for air-gapped bootstrapping, making it a reference implementation for the community.

Official Air-Gapped Workflow

Phase 1: Preparation (Internet-Connected Environment)

Download Kubernetes Images

# Download all required container images
kubeadm config images pull

# List downloaded images
kubeadm config images list

# Save images to tar archive
docker save $(kubeadm config images list) -o k8s-images.tar

Download Kubernetes Binaries

# Download kubeadm, kubelet, kubectl
K8S_VERSION=v1.28.0
ARCH=amd64

curl -LO "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/${ARCH}/kubeadm"
curl -LO "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/${ARCH}/kubelet"
curl -LO "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/${ARCH}/kubectl"

# Download checksums
curl -LO "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/${ARCH}/kubeadm.sha256"
curl -LO "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/${ARCH}/kubelet.sha256"
curl -LO "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/${ARCH}/kubectl.sha256"

# Verify checksums
sha256sum -c kubeadm.sha256
sha256sum -c kubelet.sha256
sha256sum -c kubectl.sha256

Download CNI Plugins

# Download CNI plugins
CNI_VERSION=v1.3.0
curl -LO "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-${ARCH}-${CNI_VERSION}.tgz"

Create Offline Bundle

# Create directory structure
mkdir -p k8s-offline-bundle/{images,binaries,cni,scripts}

# Move files
mv k8s-images.tar k8s-offline-bundle/images/
mv kubeadm kubelet kubectl k8s-offline-bundle/binaries/
mv cni-plugins-linux-${ARCH}-${CNI_VERSION}.tgz k8s-offline-bundle/cni/

# Create transfer script
cat > k8s-offline-bundle/transfer.sh << 'EOF'
#!/bin/bash
# Script to transfer bundle to air-gapped environment
# Usage: ./transfer.sh <air-gapped-host>
scp -r k8s-offline-bundle/ $1:/tmp/
EOF
chmod +x k8s-offline-bundle/transfer.sh

Phase 2: Transfer to Air-Gapped Environment

# Transfer bundle using secure method
# Option 1: USB drive
cp -r k8s-offline-bundle/ /media/usb-drive/

# Option 2: Secure file transfer (if isolated network link exists)
./k8s-offline-bundle/transfer.sh air-gapped-host

# Option 3: Optical media
tar -czf k8s-offline-bundle.tar.gz k8s-offline-bundle/
# Burn to DVD/Blu-ray

Phase 3: Installation (Air-Gapped Environment)

Set Up Private Container Registry

# Option 1: Harbor (recommended for production)
# Install Harbor on air-gapped host
# See Harbor documentation for offline installation

# Option 2: Simple Docker registry
docker run -d -p 5000:5000 --name registry \
  -v /var/lib/registry:/var/lib/registry \
  registry:2

Load Images into Private Registry

# Load images from tar archive
docker load -i /tmp/k8s-offline-bundle/images/k8s-images.tar

# Tag and push to private registry
PRIVATE_REGISTRY=private-registry.local:5000
for image in $(docker images --format "{{.Repository}}:{{.Tag}}" | grep k8s.gcr.io); do
  docker tag $image ${PRIVATE_REGISTRY}/${image#k8s.gcr.io/}
  docker push ${PRIVATE_REGISTRY}/${image#k8s.gcr.io/}
done

Install Kubernetes Binaries

# Install binaries
sudo cp /tmp/k8s-offline-bundle/binaries/kubeadm /usr/local/bin/
sudo cp /tmp/k8s-offline-bundle/binaries/kubelet /usr/local/bin/
sudo cp /tmp/k8s-offline-bundle/binaries/kubectl /usr/local/bin/

# Make executable
sudo chmod +x /usr/local/bin/kubeadm
sudo chmod +x /usr/local/bin/kubelet
sudo chmod +x /usr/local/bin/kubectl

Install CNI Plugins

# Extract CNI plugins
sudo mkdir -p /opt/cni/bin
sudo tar -xzf /tmp/k8s-offline-bundle/cni/cni-plugins-linux-amd64-v1.3.0.tgz -C /opt/cni/bin

Initialize Cluster with Private Registry

# Create kubeadm configuration
cat > kubeadm-config.yaml << EOF
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.28.0
imageRepository: private-registry.local:5000
networking:
  podSubnet: 10.244.0.0/16
  serviceSubnet: 10.96.0.0/12
controlPlaneEndpoint: "192.168.1.100:6443"
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: "192.168.1.100"
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///var/run/containerd/containerd.sock
EOF

# Initialize cluster
sudo kubeadm init --config=kubeadm-config.yaml

Security Considerations

Image Verification

# Verify image signatures (if available)
cosign verify --key cosign.pub k8s.gcr.io/kube-apiserver:v1.28.0

# Verify binary checksums
sha256sum -c kubeadm.sha256
sha256sum -c kubelet.sha256
sha256sum -c kubectl.sha256

Certificate Management

# Generate certificates offline
kubeadm init phase certs all --config kubeadm-config.yaml

# Backup certificates
sudo tar -czf k8s-certs.tar.gz /etc/kubernetes/pki

# Store securely (encrypted storage, secure backup location)

Network Security

  • Private Registry: Use TLS for private registry connections.
  • Network Isolation: Ensure air-gapped network is properly isolated.
  • Access Control: Restrict access to air-gapped environment.
  • Audit Logging: Enable audit logging for compliance.

CNI Plugin Installation

Calico (Offline)

# Download Calico manifests (on internet-connected machine)
curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/calico.yaml -o calico.yaml

# Modify image references
sed -i 's|docker.io/calico|private-registry.local:5000/calico|g' calico.yaml

# Transfer to air-gapped environment
# Apply on air-gapped cluster
kubectl apply -f calico.yaml

Flannel (Offline)

# Download Flannel manifest
curl https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml -o flannel.yaml

# Modify image references
sed -i 's|docker.io/flannel|private-registry.local:5000/flannel|g' flannel.yaml

# Transfer and apply
kubectl apply -f flannel.yaml

Add-On Installation

CoreDNS (Offline)

# Download CoreDNS manifest
kubectl get configmap coredns -n kube-system -o yaml > coredns-configmap.yaml

# Modify image references
sed -i 's|k8s.gcr.io/coredns|private-registry.local:5000/coredns|g' coredns-configmap.yaml

# Apply
kubectl apply -f coredns-configmap.yaml

High Availability Setup

External etcd (Air-Gapped)

# Set up external etcd cluster (3 nodes)
# Download etcd binaries
ETCD_VERSION=v3.5.9
curl -LO "https://github.com/etcd-io/etcd/releases/download/${ETCD_VERSION}/etcd-${ETCD_VERSION}-linux-amd64.tar.gz"

# Transfer to air-gapped environment
# Extract and install on etcd nodes

# Configure kubeadm for external etcd
cat > kubeadm-config-ha.yaml << EOF
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.28.0
imageRepository: private-registry.local:5000
etcd:
  external:
    endpoints:
    - https://etcd-1:2379
    - https://etcd-2:2379
    - https://etcd-3:2379
    caFile: /etc/kubernetes/pki/etcd/ca.crt
    certFile: /etc/kubernetes/pki/etcd/etcd.crt
    keyFile: /etc/kubernetes/pki/etcd/etcd.key
EOF

Update Strategies

Downloading Updates

# On internet-connected machine
# Download new Kubernetes version
K8S_VERSION=v1.29.0
kubeadm config images pull --kubernetes-version ${K8S_VERSION}

# Save images
docker save $(kubeadm config images list --kubernetes-version ${K8S_VERSION}) -o k8s-images-v1.29.0.tar

# Download binaries
curl -LO "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/amd64/kubeadm"
curl -LO "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/amd64/kubelet"
curl -LO "https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/amd64/kubectl"

Applying Updates

# On air-gapped cluster
# Load new images
docker load -i k8s-images-v1.29.0.tar

# Tag and push to private registry
# (same process as initial installation)

# Upgrade cluster
sudo kubeadm upgrade plan
sudo kubeadm upgrade apply v1.29.0

Practical Considerations

Planning Checklist

Before deploying air-gapped clusters:

  1. Inventory Requirements: List all required images, binaries, and dependencies.
  2. Registry Setup: Configure and populate private container registry.
  3. Network Planning: Plan internal DNS and network connectivity.
  4. Image Verification: Verify and sign all container images.
  5. Documentation: Document all manual steps and configurations.
  6. Testing: Test the entire process in a non-production environment first.

Transfer Methods

  • USB Drives: Physical transfer for initial setup (encrypt drives).
  • Internal Network: If air-gapped network has internal connectivity.
  • Secure File Transfer: SFTP/SCP over isolated network links.
  • Optical Media: DVDs or Blu-ray for large image sets.

Troubleshooting

Common issues and solutions:

  • “Image pull errors”: Verify private registry is accessible and images are properly tagged.
  • “DNS resolution failures”: Configure internal DNS for private registry hostname.
  • “Certificate errors”: Ensure TLS certificates are properly configured for private registry.

Conclusion

The official kubeadm air-gapped bootstrapping guide published in October 2023 provided a standardized, tested approach for deploying Kubernetes in disconnected environments. It addressed a critical need for organizations with strict security requirements, making air-gapped deployments more accessible and reliable.

The guide demonstrated that air-gapped Kubernetes deployments were not just possible but practical with proper planning and tooling. It provided teams with a reference implementation they could follow with confidence, reducing deployment risks and security vulnerabilities.

For organizations requiring air-gapped deployments, the official guide represented a significant milestone: Kubernetes was now officially supported in the most challenging deployment scenarios. This opened up container orchestration to use cases that previously seemed impossible, from government systems to industrial control systems to secure research environments.