Skip to main content
RunBook Academy

KubernetesLXXIV · kubeadmkubeadm

kubeadm init — phases, output, and post-init sanity

Advanced⏱ ~18 minkubeadmkubectl

What you'll learn

  • Run kubeadm init with the right options
  • Walk each of the 12 phases and what they produce
  • Capture the join command and the admin kubeconfig
  • Validate the cluster post-init

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

kubeadm init is the canonical entry point for bootstrapping a Kubernetes cluster. The command runs a sequence of phases — generating certificates, writing control-plane static pods, configuring etcd, setting up kubeconfig, applying initial RBAC and add-ons. This lesson walks the phases and the post-init validation.

The command

sudo kubeadm init \
  --control-plane-endpoint "loadbalancer.example:6443" \
  --upload-certs \
  --pod-network-cidr=10.244.0.0/16 \
  --kubernetes-version=v1.34.0

Each flag controls:

  • --control-plane-endpoint: the LB DNS/IP used for the cluster’s API server endpoint (the kubeconfig’s server).
  • --upload-certs: enables the join command to use encrypted certificate distribution (rotates secrets for subsequent joins).
  • --pod-network-cidr: the CIDR for Pod IPs (must match the CNI’s configuration).
  • --kubernetes-version: pin to a specific minor.

The 12 phases

flowchart LR
    A[init] --> P1[1. certs]
    P1 --> P2[2. kubeconfig]
    P2 --> P3[3. control-plane]
    P3 --> P4[4. etcd]
    P4 --> P5[5. wait-for-control-plane]
    P5 --> P6[6. bootstrap-token]
    P6 --> P7[7. self-hosted]
    P7 --> P8[8. addon]
    P8 --> P9[9. upload-certs]
    P9 --> P10[10. mark-control-plane]
    P10 --> P11[11. kubelet-start]
    P11 --> P12[12. post-init]
# Run with verbose to see each phase
sudo kubeadm init --v=2 ...

Each phase’s purpose:

Phase 1 — certs

kubeadm generates the cluster’s PKI: the CA, server certs, peer certs, etc. The CA is stored in /etc/kubernetes/pki/ca.crt. Each cert in /etc/kubernetes/pki/apiserver.crt, /etc/kubernetes/pki/apiserver.key.

Phase 2 — kubeconfig

The admin kubeconfig at /etc/kubernetes/admin.conf. The kubelet’s kubeconfig at /etc/kubernetes/kubelet.conf (using the bootstrap token).

Phase 3 — control-plane static pods

The static pod manifests are written to /etc/kubernetes/manifests/:

  • kube-apiserver.yaml
  • kube-controller-manager.yaml
  • kube-scheduler.yaml
  • etcd.yaml

The kubelet picks them up and starts the pods.

Phase 4 — etcd

The static etcd pod is configured with the cluster’s initial membership. For a single init, that’s one member; subsequent kubeadm join --control-plane adds members.

Phase 5 — wait-for-control-plane

kubeadm waits for the API server to be ready. Health check on https://<endpoint>/healthz.

Phase 6 — bootstrap-token

kubeadm creates a bootstrap-token Secret in the kube-system namespace. New nodes use this token to authenticate and fetch cluster info via CSR.

Phase 7 — self-hosted

kubeadm creates a ClusterConfiguration ConfigMap in kube-system. The kubelet reconciler on each node reads this ConfigMap and applies it.

Phase 8 — addon

kubeadm installs the cluster’s add-ons:

  • kube-proxy DaemonSet.
  • coredns Deployment.

(These can be replaced; many clusters use Cilium’s CNI-specific kube-proxy replacement.)

Phase 9 — upload-certs (with —upload-certs)

kubeadm encrypts the cluster’s certificates and uploads them as a Secret in kube-system. Subsequent join commands download and decrypt them.

Phase 10 — mark-control-plane

Labels the host as a control-plane node. The kubelet on control-plane nodes runs taints like node-role.kubernetes.io/control-plane.

Phase 11 — kubelet-start

The kubelet is restarted (or started) on the host with the new configuration.

Phase 12 — post-init

kubeadm prints the join command and the admin kubeconfig path.

The output

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f <network-addon.yaml>" on the master.

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join loadbalancer.example:6443 \
  --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:...

The output contains:

  • The admin kubeconfig location.
  • The join command for additional nodes.
  • The token and CA cert hash needed for the join.
Cluster-wide risk
$ sudo kubeadm init --control-plane-endpoint loadbalancer.example:6443 --pod-network-cidr=10.244.0.0/16 --kubernetes-version=v1.34.0 2>&1 | tail -30
...

The post-init sanity

After init, validate:

# 1. kubectl is configured
kubectl get nodes
# Expected: cp-1 listed, status NotReady (CNI not installed yet)

# 2. The control-plane components are healthy
kubectl get pods -n kube-system
# Expected: kube-apiserver, kube-controller-manager, kube-scheduler, etcd all Running

# 3. The kubeadm-config ConfigMap exists
kubectl -n kube-system get configmap kubeadm-config -o yaml

Install the CNI

Until the CNI is installed, nodes are NotReady (no Pod network):

# Install a CNI plugin
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml

# Or:
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

After CNI install, nodes become Ready.

The init variations

External etcd

sudo kubeadm init \
  --control-plane-endpoint loadbalancer.example:6443 \
  --etcd-servers "https://etcd-1:2379,https://etcd-2:2379,https://etcd-3:2379" \
  --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt \
  --etcd-certfile=/etc/kubernetes/pki/etcd/client.crt \
  --etcd-keyfile=/etc/kubernetes/pki/etcd/client.key

kubeadm connects to the external etcd cluster instead of running one locally.

HA control plane with —upload-certs

sudo kubeadm init \
  --control-plane-endpoint loadbalancer.example:6443 \
  --upload-certs \
  --pod-network-cidr=10.244.0.0/16

The --upload-certs flag enables the join command to auto-fetch certificates.

The re-init

kubeadm init is idempotent only if the cluster state is clean. To re-init:

sudo kubeadm reset
sudo rm -rf /etc/kubernetes/pki /var/lib/etcd
sudo kubeadm init ...

The kubeadm reset undoes init’s effects on a host. It removes static pods, certificates, and the local etcd data dir.

The cluster-info

After init, the cluster’s discovery info:

kubectl cluster-info
Kubernetes control plane is running at https://loadbalancer.example:6443
CoreDNS is running at https://loadbalancer.example:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

The cluster-info points at the LB; the API server is running.

Quiz

Knowledge check · 4 questions

  1. Q1. After `kubeadm init`, what does the join command look like?

  2. Q2. `kubeadm init` installs the cluster CNI plugin as part of its phases.

  3. Q3. kubeadm init fails at Phase 8 (addon). Diagnose and remediate.

    The init proceeds through certs, control-plane pods, etcd. At Phase 8 (addon installation of kube-proxy), the API server has an RBAC misconfiguration.

  4. Q4. What is the operator's responsibility after `kubeadm init` completes?

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • Document the init flags. Replicating a cluster requires exact flags.
  • Capture the join command. It’s the only time the token is in plain text.
  • Install CNI immediately. No Pod network without it.
  • Validate post-init. kubectl get nodes, get pods -n kube-system.
  • Re-init only after reset. A dirty state confuses init.
  • Capture the kubeadm-config ConfigMap. It’s the cluster’s configuration record.

kubeadm init is the cluster’s first hour. Operating it well is the foundation that every later operation builds on.