9 RKE2 Control Plane Setup
gitchegumi edited this page 2026-08-29 09:37:50 -05:00

RKE2 Control Plane Setup

This runbook documents a completed RKE2 control-plane installation and the subsequent WSL kubectl configuration. I chose a NucBox G5 for my control-plane node, but the procedure is intended to be reusable with another Ubuntu host.

Prerequisite: This walkthrough assumes a basic knowledge of Kubernetes. If any terminology is unfamiliar, see Acronyms and abbreviations at the end.

Replace these placeholders with values for your environment:

Placeholder Meaning
<user-name> Linux account on the control-plane host
<control-plane-ip> Control-plane host's LAN IP address
<node-name> Kubernetes node name
<control-plane-hostname> Hostname included in the Kubernetes API certificate
<physical-lan-interface> WSL interface that reaches the physical LAN
<conflicting-interface> WSL or Hyper-V interface incorrectly claiming the same LAN subnet
<physical-lan-cidr> Physical LAN subnet in CIDR notation

NOTE: Do not paste angle-bracket placeholders into commands or configuration unchanged.

Example deployment

Item Choice
Host NucBox G5
OS Ubuntu 24.04 LTS
CPU Intel N97
RAM Approximately 12 GB
Role RKE2 server, etcd, and Kubernetes control plane
Installed Kubernetes version v1.35.7+rke2r1

The node was given a control-plane taint so normal application workloads cannot schedule there. Services that share the same physical host but run outside Kubernetes remain outside the Kubernetes failure boundary.

The RKE2 version was not pinned. The standard installer selected v1.35.7+rke2r1 at the time of this deployment; a later rebuild may install a newer version.

1. Update the control-plane host

Run on the control-plane host:

sudo apt update
sudo apt upgrade -y

2. Verify the networking backend

Check whether the host uses systemd-networkd:

systemctl is-active systemd-networkd

If the command returns active, continue to the next step. If it does not, the host may use NetworkManager and require a Canal networking workaround that is beyond the scope of this walkthrough.

3. Become root and install RKE2

Run on the control-plane host:

sudo -s

The prompt should now indicate a root shell. Run the standard installation command from the official RKE2 documentation directly as root:

curl -sfL https://get.rke2.io | sh -

NOTE: Check the RKE2 Official Documentation if this command doesn't work. It may have changed.

This installation did not set INSTALL_RKE2_VERSION. Remain in the root shell for the configuration and startup steps.

4. Create the RKE2 configuration

Create the configuration directory:

mkdir -p /etc/rancher/rke2

Create /etc/rancher/rke2/config.yaml, replacing each placeholder with the selected value:

write-kubeconfig-mode: "0644"

node-name: "<node-name>"

tls-san:
  - "<control-plane-ip>"
  - "<control-plane-hostname>"

node-taint:
  - "node-role.kubernetes.io/control-plane=true:NoSchedule"

cni: canal

ingress-controller: none
  • node-name controls the name displayed by kubectl get nodes.
  • tls-san makes the API certificate valid for the chosen IP address and hostname.
  • The taint keeps ordinary workloads off the control plane.
  • Canal is the RKE2 CNI.
  • The bundled ingress controller is disabled because UDS Core will eventually provide Istio tenant and admin gateways.

5. Start RKE2

Still in the root shell:

systemctl enable --now rke2-server

Watch startup if needed:

journalctl -u rke2-server -f

Exit the root shell when finished:

exit

6. Verify the control plane locally

Run on the control-plane host:

sudo /var/lib/rancher/rke2/bin/kubectl get nodes
sudo /var/lib/rancher/rke2/bin/kubectl get pods -A

Expected node state:

NAME          STATUS   ROLES                VERSION
<node-name>   Ready    control-plane,etcd   <installed-version>

In this deployment, the installed version was v1.35.7+rke2r1.

After the initial installation, several RKE2 Helm installer pods remained Pending, including metrics-server, runtimeclasses, and snapshot-controller jobs. This was expected because the control-plane node was tainted NoSchedule and the cluster did not yet have any worker nodes. Those workloads will have somewhere to schedule after the first worker joins.

7. Protect the worker join token

The RKE2 worker join token is stored on the control-plane host at:

sudo cat /var/lib/rancher/rke2/server/node-token

Warning: This token grants cluster-join access. Never commit it to a public repository or wiki.

8. Configure kubectl in WSL

The Windows workstation in this deployment already had a kubeconfig from an older Hyper-V Kubernetes cluster. The RKE2 configuration was therefore added without unnecessarily destroying the existing configuration.

Create a temporary copy on the control-plane host

Run on the control-plane host after setting your account name:

USER_NAME="<user-name>"
sudo cp /etc/rancher/rke2/rke2.yaml "/home/${USER_NAME}/rke2.yaml"
sudo chown "${USER_NAME}:${USER_NAME}" "/home/${USER_NAME}/rke2.yaml"

Copy and update it in WSL

Run in WSL after setting the control-plane account and IP:

USER_NAME="<user-name>"
CONTROL_PLANE_IP="<control-plane-ip>"
scp "${USER_NAME}@${CONTROL_PLANE_IP}:/home/${USER_NAME}/rke2.yaml" ~/.kube/rke2.yaml
sed -i "s/127\.0\.0\.1/${CONTROL_PLANE_IP}/" ~/.kube/rke2.yaml

The generated RKE2 kubeconfig defaults to https://127.0.0.1:6443. A remote management client must instead use https://<control-plane-ip>:6443.

Merge with the existing kubeconfig

First identify the current configuration location:

echo $KUBECONFIG

For an existing ~/.kube/config, merge and flatten both configurations:

KUBECONFIG=~/.kube/config:~/.kube/rke2.yaml \
kubectl config view --flatten > ~/.kube/config.merged

After verifying the merged file:

mv ~/.kube/config.merged ~/.kube/config

If the existing kubeconfig uses another filename, such as ~/.kube/config.yaml, use that actual path in both the merge command and final mv instead.

List the available contexts:

kubectl config get-contexts

If necessary, rename the new RKE2 context to homelab:

kubectl config rename-context <current-rke2-context> homelab

Select and verify it:

kubectl config use-context homelab
kubectl get nodes
kubectl get pods -A

Expected remote node state:

NAME          STATUS   ROLES                AGE   VERSION
<node-name>   Ready    control-plane,etcd   ...   <installed-version>

Optional troubleshooting from this deployment

The SSH and routing problems below were specific issues encountered while configuring this homelab. They are not required RKE2 installation steps, and many environments will not encounter them. Use these checks only when the symptoms match.

SSH public-key authentication

ssh-copy-id initially failed because the control-plane host allowed only public-key authentication:

Permission denied (publickey).

The WSL public key was copied manually. Do not copy a private key.

Run in WSL to display the public key:

cat ~/.ssh/id_ed25519.pub

Run as the target account on the control-plane host:

mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Paste only the WSL public key into authorized_keys, then verify from WSL:

ssh <user-name>@<control-plane-ip>

Duplicate WSL route

WSL initially had two interfaces advertising the same physical LAN subnet:

<physical-lan-cidr> dev <conflicting-interface>
<physical-lan-cidr> dev <physical-lan-interface>

A Hyper-V-related interface incorrectly occupied the physical LAN subnet. Traffic to the control-plane host sometimes used the wrong interface and returned:

Destination Host Unreachable

Verify in WSL:

ip route get <control-plane-ip>
ping -I <physical-lan-interface> -c 3 <control-plane-ip>
ping -I <conflicting-interface> -c 3 <control-plane-ip>

The temporary fix was to remove the duplicate route from the conflicting interface:

sudo ip route del <physical-lan-cidr> dev <conflicting-interface>

The permanent solution is to ensure Hyper-V and internal virtual networks do not use the same subnet as the physical LAN. Do not change the physical LAN subnet solely to work around a conflicting virtual network.

Deployment outcome

  • RKE2 control plane operational.
  • Control-plane node Ready.
  • Installed version in this deployment: v1.35.7+rke2r1.
  • Control plane tainted against normal workloads.
  • RKE2 bundled ingress disabled.
  • Canal networking operational.
  • WSL kubectl access operational.
  • Next task: join the first RKE2 worker.
  • Planned worker roles include general compute, game-server capacity, and GPU workloads.

Acronyms and abbreviations

Term Meaning
API Application Programming Interface
CIDR Classless Inter-Domain Routing
CNI Container Network Interface
CPU Central Processing Unit
GB Gigabyte
GPU Graphics Processing Unit
HTTPS Hypertext Transfer Protocol Secure
IP Internet Protocol
LAN Local Area Network
LTS Long-Term Support
OS Operating System
RAM Random Access Memory
RKE2 Rancher Kubernetes Engine 2
SAN Subject Alternative Name
SCP Secure Copy Protocol
SSH Secure Shell
TLS Transport Layer Security
UDS Unified Defense Stack
WSL Windows Subsystem for Linux
YAML YAML Ain't Markup Language