Skip to content

How to Add a Bare-Metal Worker Node (Ansible)

This guide details the explicit, step-by-step playbook sequence necessary to provision and join a fresh bare-metal Debian machine into our Kubernetes cluster, using the strict numerical sequence defined in our homelab architecture.

Prerequisites

Before executing these playbooks, ensure the following conditions are met:

  • The new node has Debian installed (e.g., Debian 13 Trixie).
  • The node is connected to the same Layer 2 network switch.
  • You have noted the IP address assigned to the new node by your DHCP server, and defined it in your Ansible hosts.yaml inventory.

Execution Sequence

The homelab project enforces a strict, reproducible execution order (numbered 00 through 09).

1. Initial Access & Escalation

Fresh minimal Debian installations often lack the sudo package, and direct root SSH access is typically disabled. Our initial playbooks gracefully handle standard SSH access and utilize su for root escalation.

  • Step 1: Distribute SSH Keys Push your automation user's public SSH key to the new node so subsequent playbooks don't require manual SSH passwords.
ansible-playbook -i ansible/inventory/hosts.yaml ansible/playbooks/00-setup-ssh.yaml -l <new-node-name> --ask-pass
  • Step 2: Install Sudo Elevate privileges using become_method: su to install sudo and authorize the automation user.
ansible-playbook -i ansible/inventory/hosts.yaml ansible/playbooks/01-setup-sudo.yaml -l <new-node-name> --ask-pass --ask-become-pass

!!! info "Important" You will be prompted for both the standard user password (SSH password) and the root password (BECOME password).

2. Core Node Provisioning

Now that passwordless sudo is configured, you can execute the core bootstrap sequence using --ask-become-pass (or -K).

  • Step 3: Bootstrap Debian Hardens the OS, configures kernel parameters, disables swap memory, and sets the physical hostname.
ansible-playbook -i ansible/inventory/hosts.yaml ansible/playbooks/02-bootstrap-debian.yaml -l <new-node-name> -K
  • Step 4: Install Container Runtime Installs containerd and configures it to utilize systemd cgroups.
ansible-playbook -i ansible/inventory/hosts.yaml ansible/playbooks/03-install-containerd.yaml -l <new-node-name> -K
  • Step 5: Install Kubernetes Binaries Installs kubeadm, kubelet, and kubectl. This step includes mandatory dependencies like conntrack and ethtool to pass kubeadm preflight checks.
ansible-playbook -i ansible/inventory/hosts.yaml ansible/playbooks/04-install-k8s.yaml -l <new-node-name> -K

3. Join the Cluster

Once the core binaries are in place, physically join the node to the control plane.

  • Step 6: Generate Join Token SSH into the control plane node and generate the join command:
sudo kubeadm token create --print-join-command
  • Step 7: Execute Join Command SSH into the new worker node and execute the generated string:
sudo kubeadm join 192.168.1.51:6443 --token <short-token> --discovery-token-ca-cert-hash sha256:<long-hash>

!!! warning "Common Error" Ensure you do not accidentally pass the sha256:... hash into the --token parameter, as it will trigger an invalid token preflight failure.

4. Post-Join Configuration

  • Step 8: Apply Role and Hardware Labels From your admin workstation, run the custom labeling script to categorize the node.
./scripts/label-nodes.sh
  • Step 9: Optional Hardware Add-ons If the node requires specific roles (e.g., VPN, GPU), execute the respective playbooks (07-install-tailscale.yaml or 08-install-nvidia.yaml).

Architecture Notes

By encapsulating these disparate configuration tasks into sequential, idempotent playbooks, we conform to the principle of Infrastructure as Code (IaC). The node can be completely wiped and re-provisioned in minutes, minimizing downtime during hardware migrations.