Installing k3s on baremetal - step by step walkthrough and with terraform

Installing little k3s kubernetes on homelab cluster

Page content

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.

kubernetes baremetal cluster

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, and sudo 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 a kubectl installed as k3s kubectl or symlinked as kubectl.


πŸ“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.
  • Systemd Services:

    • sudo systemctl status k3s
    • sudo systemctl status k3s-agent (on workers)

πŸ’‘ Extras (Optional)

  • K9s: Terminal UI for managing Kubernetes: brew install k9s or sudo 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 use local-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.