Installing k3s on baremetal - step by step walkthrough and with terraform
Installing little k3s kubernetes on homelab cluster
Hereβs a step-by-step walkthrough of installation of a 3-node K3s cluster on bare-metal servers (1 master + 2 workers).
This guide assumes you’re starting with a minimal Linux install (e.g., Ubuntu 22.04 or Debian) on each server, and want to self-host your control plane and workloads.
K3s is a lightweight flavour of Kubernetes, suilts well for this little homelab.
βοΈ Cluster Topology
Node Name | Role | Hostname/IP |
---|---|---|
master-1 |
Server (Control Plane) | 192.168.1.100 |
worker-1 |
Agent (Worker Node) | 192.168.1.101 |
worker-2 |
Agent (Worker Node) | 192.168.1.102 |
β Prerequisites
π§± Hardware (Minimum):
- CPU: 2 cores
- RAM: 2GB+ per node
- Disk: 20GB+ per node
π OS Requirements:
- Linux (Ubuntu 22.04, Debian 12, etc.)
curl
,ssh
, andsudo
installed- Root or sudo access
π§Ό Clean Networking:
-
Ensure all nodes can resolve and ping each other by IP or hostname.
-
Disable swap:
sudo swapoff -a sudo sed -i '/ swap / s/^/#/' /etc/fstab
π Step-by-Step Installation
πStep 1: Install K3s on the Control Plane (master-1)
curl -sfL https://get.k3s.io | sh -
After installation:
sudo cat /var/lib/rancher/k3s/server/node-token
- This gives you the join token needed for worker nodes.
Confirm the cluster is running:
sudo kubectl get nodes
k3s
includes akubectl
installed ask3s kubectl
or symlinked askubectl
.
πStep 2: Install K3s Agents (worker-1 & worker-2)
Repeat on each worker node:
Replace <MASTER_IP>
and <NODE_TOKEN>
with values from step 1.
curl -sfL https://get.k3s.io | K3S_URL=https://192.168.1.100:6443 K3S_TOKEN=<NODE_TOKEN> sh -
Examples:
K3S_URL=https://192.168.1.100:6443 \
K3S_TOKEN=K10abcde123456789::server:abcde123456789 \
sh -c "$(curl -sfL https://get.k3s.io)"
πStep 3: Verify the Cluster from master-1
kubectl get nodes
You should see:
NAME STATUS ROLES AGE VERSION
master-1 Ready control 5m v1.29.2+k3s1
worker-1 Ready <none> 2m v1.29.2+k3s1
worker-2 Ready <none> 2m v1.29.2+k3s1
π (Optional) Enable High Availability (HA)
For production-like HA (optional in home labs):
- Use an external DB (e.g., MySQL/PostgreSQL/etcd).
- Run multiple server nodes with
--server
flag and shared DB. - See: K3s HA Doc
π§ͺ Test a Deployment
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=LoadBalancer
kubectl get svc
Check access via curl http://<worker-ip>:<port>
or set up MetalLB for external LoadBalancer IPs.
π§° Helpful Notes
-
Kubeconfig:
/etc/rancher/k3s/k3s.yaml
- Copy to your local machine and
export KUBECONFIG=...
to manage remotely.
- Copy to your local machine and
-
Systemd Services:
sudo systemctl status k3s
sudo systemctl status k3s-agent
(on workers)
π‘ Extras (Optional)
-
K9s: Terminal UI for managing Kubernetes:
brew install k9s
orsudo snap install k9s
-
Dashboard: Enable with manifests:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
-
MetalLB: Add load balancing support for bare-metal:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.10/config/manifests/metallb-native.yaml
π¦ Cleanup
To remove k3s:
# On master:
/usr/local/bin/k3s-uninstall.sh
# On worker:
/usr/local/bin/k3s-agent-uninstall.sh
Install 3-node bare-metal K3s cluster with Terraform
Here is a Terraform template that helps provision a 3-node bare-metal K3s cluster assuming you’re managing already-provisioned Linux servers (e.g., in your LAN/home lab) via SSH and Ansible. Terraform will invoke Ansible playbooks to install K3s.
π§± Assumptions
-
You already have:
- 3 servers with fixed IPs, accessible via SSH.
- SSH access (key-based).
- Terraform and Ansible installed locally.
-
Target OS: Ubuntu/Debian (can be customized).
-
You’re using Terraform
local-exec
or Ansible provisioner plugin (weβll uselocal-exec
here for portability).
π Directory Structure
k3s-cluster/
βββ main.tf
βββ inventory.ini
βββ ansible/
β βββ install_k3s.yml
βββ variables.tf
π variables.tf
variable "master_ip" {
default = "192.168.1.100"
}
variable "worker_ips" {
default = ["192.168.1.101", "192.168.1.102"]
}
variable "ssh_user" {
default = "your_user"
}
variable "ssh_private_key" {
default = "~/.ssh/id_rsa"
}
π main.tf
provider "null" {}
resource "null_resource" "install_k3s" {
provisioner "local-exec" {
command = "ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i inventory.ini ansible/install_k3s.yml --private-key ${var.ssh_private_key} -u ${var.ssh_user}"
}
triggers = {
always_run = "${timestamp()}"
}
}
π inventory.ini
[masters]
192.168.1.100
[workers]
192.168.1.101
192.168.1.102
[all:vars]
ansible_python_interpreter=/usr/bin/python3
π ansible/install_k3s.yml
---
- hosts: masters
become: yes
tasks:
- name: Install k3s master
shell: |
curl -sfL https://get.k3s.io | sh -
- name: Get node-token
shell: cat /var/lib/rancher/k3s/server/node-token
register: node_token
delegate_to: localhost
- hosts: workers
become: yes
tasks:
- name: Join worker to cluster
shell: |
curl -sfL https://get.k3s.io | K3S_URL=https://{{ hostvars['192.168.1.100']['ansible_host'] }}:6443 K3S_TOKEN={{ hostvars['192.168.1.100']['node_token']['stdout'] }} sh -
Note: You might need to tweak IP or token variable access depending on Ansible versions. You can also generate token on master and save it to a file before running worker install.
π How to Run
From the k3s-cluster/
directory:
terraform init
terraform apply
π§° Enhancements (Optional)
- Use dynamic inventory generation (e.g., from DHCP/dnsmasq).
- Add MetalLB deployment via Ansible.
- Add hostnames and HA features.
- Replace with Ansible role like
xanmanning.k3s
for more flexibility.
Useful links
- https://k3s.io/
- Kubernetes Cheatsheet
- Install Kubernetes with Kubespray
- Terraform cheatsheet - useful commands and examples
- Kubernetes distributions - quick overview of kubeadm, k3s, MicroK8s, Minikube, Talos Linux and RKE2
- Comparison of Kubernetes Distributions for a 3-Node Homelab
- How to Install Ubuntu 24.04 & useful tools
- Docker Cheatsheet
- Docker Compose Cheatsheet - Most useful commands with examples