Join a Worker Node to the Cluster with kubeadm join
A control plane is healthy, but the prepared worker has not joined it. The worker's SSH hostname is in `/root/worker-name.txt`; root SSH access from this terminal is configured. `/root/worker-join.yaml` is a kubeadm configuration with three discovery placeholders. Its remaining host settings are already correct.
Live lab access
This task description is public. Its live lab requires full access; six other CKA labs are available in Free Practice with AI Lab Coach.
Create an account to continue →
Explore the six free labs with AI Lab Coach →
Read the brief on any device. Use a desktop or laptop for the terminal lab. Nothing starts until you choose Start.
The task
A control plane is healthy, but the prepared worker has not joined it. The worker's SSH hostname is in /root/worker-name.txt; root SSH access from this terminal is configured. /root/worker-join.yaml is a kubeadm configuration with three discovery placeholders. Its remaining host settings are already correct.
Requirements
1. Generate a valid short-lived bootstrap token and obtain the API endpoint and discovery CA hash from this control plane.
2. Replace the token, endpoint, and CA-hash placeholders in /root/worker-join.yaml. Copy the configuration to the worker and use it to join that worker to this cluster.
3. Ensure the intended worker is Ready and schedulable.
4. Label the worker team=platform.
Keep CA verification enabled and preserve the supplied non-discovery host settings.
Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster. Run the join only on a throwaway VM that has a container runtime and matching kubeadm and kubelet packages, never on a production node.
Bootstrap tokens and CA pinning: how a new node learns to trust the cluster
Joining a node needs trust in both directions. In discovery, the node fetches cluster details and checks that it is talking to the real control plane. In TLS bootstrap, the control plane lets the node authenticate briefly with a shared token so its kubelet can request a client certificate. One short-lived bootstrap token drives both halves, and the CA hash is what stops an impostor API server from answering discovery.
worker control plane
kubeadm join ──token──→ reads cluster-info (kube-public)
checks CA public key against sha256 pin
kubelet ──CSR, authenticated by token──→ auto-approved ──→ node client certificate
kubelet registers Node ──→ CNI Pod starts on it ──→ Ready
1. Look at existing tokens first
A bootstrap token has the form [a-z0-9]{6}.[a-z0-9]{16}. The first part is a public token ID; the second is the secret. Each token is stored as a Secret named bootstrap-token-<id> in kube-system, and on kubeadm clusters the controller manager's TokenCleaner deletes expired ones. On the control plane, read-only:
kubeadm token list
kubectl -n kube-system get secrets --field-selector type=bootstrap.kubernetes.io/token
The token printed by kubeadm init lives 24 hours by default, so on an older cluster the list is often empty.
2. Create a short-lived token with the join command
kubeadm token create --ttl 45m --description "practice worker" --print-join-command
--print-join-command prints a complete kubeadm join line containing the API endpoint, the new token and a --discovery-token-ca-cert-hash value. A shorter --ttl limits how long a leaked token is useful. You can also compute the hash yourself from the cluster CA; it is a SHA-256 of the CA's public key, written as sha256:<hex>:
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt \
| openssl rsa -pubin -outform der 2>/dev/null \
| openssl dgst -sha256 -hex | sed 's/^.* //'
3. Map the same values into a JoinConfiguration
When a node needs settings that have no flag, the join is driven by a file. Each value from the printed command has a home under discovery.bootstrapToken. Our own example, with placeholders left as placeholders:
apiVersion: kubeadm.k8s.io/v1beta4
kind: JoinConfiguration
discovery:
bootstrapToken:
apiServerEndpoint: 10.30.0.10:6443
token: <token-id>.<token-secret>
caCertHashes:
- sha256:<64 hex characters>
nodeRegistration:
name: edge-w2
caCertHashes is a list, and leaving it empty turns off CA pinning. The unsafeSkipCAVerification field does the same deliberately, and the docs warn that it lets another machine impersonate the control plane. tlsBootstrapToken can be omitted; it defaults to the discovery token. On the worker, kubeadm join --config <file> runs the join; the kubeadm docs warn that mixing --config with other flags may not be allowed.
4. Verify from the control plane
kubectl get csr
kubectl get nodes -o wide
kubectl describe node edge-w2 | grep -E 'Unschedulable|Taints'
kubectl -n kube-system get pods -o wide --field-selector spec.nodeName=edge-w2
Expect a node client CSR that was approved automatically, then the node in the list. It may report NotReady until the network plugin's Pod is running on it, which the last command shows. A cordoned node shows SchedulingDisabled in the STATUS column; that is a display of spec.unschedulable, not a real condition.
Common mistakes
- Reusing a join line from an old notes file: the token has expired and discovery fails. Create a fresh one.
- Dropping the
sha256:prefix from the hash: the value must be<type>:<value>. - Joining with a node name that already exists: kubeadm checks for an existing Node with the same name and refuses.
- Pasting the hash of the wrong CA (for example the etcd CA or front-proxy CA): pinning fails, which is the protection doing its job.
| Example check | Evidence |
|---|---|
| Token is short-lived | kubeadm token list shows a TTL in minutes, not 23h |
| CA pinning stayed on | The join file has a caCertHashes entry and no unsafe skip |
| Node registered | kubectl get nodes lists it with STATUS Ready |
| Node accepts Pods | No SchedulingDisabled; spec.unschedulable unset |
Cleanup: kubeadm token delete <token-id> once the node has joined; to undo a practice join, drain and delete the node, then run kubeadm reset on the worker. References: kubeadm join, Authenticating with Bootstrap Tokens, kubeadm token. Next, try initializing a control plane or upgrading a kubeadm cluster.
Practice scope
This additional curriculum lab is available in single-task practice with the existing 12-minute limit. Its alternate scenario also appears in one of the two mixed curriculum mocks. The first three 17-task mocks keep their original task sets. Hints, an explained solution and requirement-by-requirement feedback are available in the lab.
Browse CKA practice tasks →