Skip to content

K3s Cluster on Proxmox

A complete, reproducible walkthrough for standing up a 3-node K3s Kubernetes cluster on Proxmox VE. We build a reusable Ubuntu cloud-init template on the Proxmox host, clone it into three VMs, then install K3s (one control plane + two workers).

The Proxmox commands below follow the official Proxmox VE Cloud-Init documentation.

Node Role IP Address Resources
k3s-control Control plane 192.168.1.101 4 vCPU / 8 GB / 50 GB
k3s-worker1 Worker 192.168.1.102 4 vCPU / 8 GB / 50 GB
k3s-worker2 Worker 192.168.1.103 4 vCPU / 8 GB / 50 GB
  • Proxmox host: 192.168.1.100
  • Gateway: 192.168.1.1
  • Kubernetes API: 192.168.1.101:6443
  • A running Proxmox VE host (8.x or 9.x) with SSH/root access.
  • Outbound internet from the host to download the Ubuntu cloud image.
  • An SSH key pair on your workstation (~/.ssh/id_ed25519.pub or id_rsa.pub).
  • A storage pool that supports disk images (this guide uses local-lvm).

Step 1 — Build the Ubuntu Cloud-Init Template

Section titled “Step 1 — Build the Ubuntu Cloud-Init Template”

Run all commands in this section on the Proxmox host.

Terminal window
# Ubuntu 24.04 LTS (Noble) cloud image
cd /var/lib/vz/template/iso
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img

We use VM ID 9000 as the template. The virtio-scsi-pci controller is required for Ubuntu cloud images.

Terminal window
# Create a VM with 2 GB RAM and a VirtIO SCSI controller
qm create 9000 --name ubuntu-2404-template --memory 2048 --cores 2 \
--net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-pci
# Import the downloaded disk to local-lvm as scsi0
qm set 9000 --scsi0 local-lvm:0,import-from=/var/lib/vz/template/iso/noble-server-cloudimg-amd64.img

1.3 Add the cloud-init drive and boot settings

Section titled “1.3 Add the cloud-init drive and boot settings”
Terminal window
# Cloud-Init needs a CD-ROM drive to pass its config
qm set 9000 --ide2 local-lvm:cloudinit
# Boot from the imported disk only (faster boot)
qm set 9000 --boot order=scsi0
# Serial console + serial display (expected by most cloud images)
qm set 9000 --serial0 socket --vga serial0
Terminal window
# Default login user created inside cloned VMs
qm set 9000 --ciuser devops
# Inject your SSH public key for key-based login
qm set 9000 --sshkey ~/.ssh/id_ed25519.pub
# Get an IP via DHCP by default (overridden per-clone below)
qm set 9000 --ipconfig0 ip=dhcp
# Enable the QEMU guest agent (recommended)
qm set 9000 --agent enabled=1
Terminal window
qm template 9000

You now have a reusable template. Linked clones from it deploy in seconds.

Still on the Proxmox host. We assign VM IDs 101–103 and static IPs.

Terminal window
# Control plane
qm clone 9000 101 --name k3s-control
qm set 101 --ipconfig0 ip=192.168.1.101/24,gw=192.168.1.1
qm set 101 --memory 8192 --cores 4
qm resize 101 scsi0 50G
# Worker 1
qm clone 9000 102 --name k3s-worker1
qm set 102 --ipconfig0 ip=192.168.1.102/24,gw=192.168.1.1
qm set 102 --memory 8192 --cores 4
qm resize 102 scsi0 50G
# Worker 2
qm clone 9000 103 --name k3s-worker2
qm set 103 --ipconfig0 ip=192.168.1.103/24,gw=192.168.1.1
qm set 103 --memory 8192 --cores 4
qm resize 103 scsi0 50G

Start all three VMs:

Terminal window
qm start 101 && qm start 102 && qm start 103

Give cloud-init 1–2 minutes on first boot, then confirm SSH works from your workstation:

Terminal window
ssh devops@192.168.1.101

SSH into k3s-control (192.168.1.101) and run:

Terminal window
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server \
--cluster-init \
--write-kubeconfig-mode 644 \
--tls-san 192.168.1.101" \
sh -
  • --cluster-init initializes an embedded etcd datastore (enables adding more servers later for HA).
  • --write-kubeconfig-mode 644 makes the kubeconfig readable so you can copy it out.
  • --tls-san adds the API IP to the server certificate.

Verify the control plane is up:

Terminal window
sudo k3s kubectl get nodes

You need this token to join the workers:

Terminal window
sudo cat /var/lib/rancher/k3s/server/node-token

SSH into each worker (192.168.1.102 and 192.168.1.103) and run, substituting the token from the previous step:

Terminal window
curl -sfL https://get.k3s.io | \
K3S_URL=https://192.168.1.101:6443 \
K3S_TOKEN="<node-token-from-control-plane>" \
sh -

The installer sets up the k3s-agent service and registers the node.

Back on the control plane:

Terminal window
sudo k3s kubectl get nodes -o wide

Expected output:

NAME STATUS ROLES AGE VERSION
k3s-control Ready control-plane,etcd,master 2m v1.31.x+k3s1
k3s-worker1 Ready <none> 1m v1.31.x+k3s1
k3s-worker2 Ready <none> 1m v1.31.x+k3s1

Check the system pods:

Terminal window
sudo k3s kubectl get pods -A

Copy the kubeconfig to your workstation and point it at the control plane IP:

Terminal window
# On your workstation
scp devops@192.168.1.101:/etc/rancher/k3s/k3s.yaml ./kubeconfig
# The file points at 127.0.0.1 — replace with the control-plane IP
sed -i '' 's/127.0.0.1/192.168.1.101/' ./kubeconfig # macOS
# sed -i 's/127.0.0.1/192.168.1.101/' ./kubeconfig # Linux
export KUBECONFIG=$PWD/kubeconfig
kubectl get nodes
Terminal window
# Control plane service status
sudo systemctl status k3s
# Worker agent status (on workers)
sudo systemctl status k3s-agent
# Follow logs
sudo journalctl -u k3s -f
Terminal window
# On the control plane
/usr/local/bin/k3s-uninstall.sh
# On a worker
/usr/local/bin/k3s-agent-uninstall.sh
Symptom Likely cause Fix
Clone has no IP cloud-init didn’t apply Confirm --ipconfig0 is set and the cloud-init CD-ROM (ide2) exists
SSH key rejected Wrong key injected into template Re-run qm set 9000 --sshkey ... and re-clone
Worker won’t join Wrong token or API unreachable Recheck the node-token and that :6443 is reachable from the worker
kubectl connection refused remotely kubeconfig points at 127.0.0.1 Replace with 192.168.1.101 and add --tls-san on install

With the cluster running you can layer on:

  • Traefik / Nginx ingress
  • Longhorn for distributed block storage
  • Argo CD for GitOps delivery
  • Prometheus + Grafana for observability (SLOs, dashboards, alerting)

Once your cluster is exposed to a team, secure access to your tooling with single sign-on — see the SSO with SAML in SonarQube guide. For more about this lab and the rest of the stack, visit the about page.


Sources: Proxmox VE Cloud-Init Support, K3s Quick-Start.