Containers

Install K3s on Ubuntu 26.04 LTS

K3s installs a production Kubernetes cluster in under 30 seconds. One binary, one command. Rancher built it for edge computing, IoT, and resource-constrained environments, but plenty of teams run it as their primary cluster because kubeadm feels like assembling furniture when all you need is a working table.

Original content from computingforgeeks.com - post 166089

This guide walks through installing K3s on Ubuntu 26.04 LTS, deploying workloads with the built-in Traefik ingress controller, adding Helm chart support, configuring remote kubectl access, and setting up high availability with embedded etcd. K3s ships with containerd, CoreDNS, Traefik, a local path provisioner, and a metrics server, all bundled into a single ~70 MB binary.

Verified working: April 2026 on Ubuntu 26.04 LTS (kernel 7.0), K3s v1.34.6+k3s1

Prerequisites

Before starting, make sure you have the following:

  • Ubuntu 26.04 LTS server with at least 2 GB RAM and 2 CPU cores (4 GB recommended for running workloads)
  • Root or sudo access
  • Tested on: Ubuntu 26.04 LTS (Resolute Raccoon), kernel 7.0.0-10-generic
  • Network connectivity to download the K3s binary from GitHub releases

If you need a baseline server configuration, follow the Ubuntu 26.04 initial server setup guide first.

Install K3s on Ubuntu 26.04

The official install script handles everything: downloading the binary, creating systemd services, and starting the cluster. One command:

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

The installer downloads the latest stable release and configures K3s as a systemd service:

[INFO]  Finding release for channel stable
[INFO]  Using v1.34.6+k3s1 as release
[INFO]  Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.34.6+k3s1/sha256sum-amd64.txt
[INFO]  Downloading binary https://github.com/k3s-io/k3s/releases/download/v1.34.6+k3s1/k3s
[INFO]  Verifying binary download
[INFO]  Installing k3s to /usr/local/bin/k3s
[INFO]  Skipping installation of SELinux RPM
[INFO]  Creating /usr/local/bin/kubectl symlink to k3s
[INFO]  Creating /usr/local/bin/crictl symlink to k3s
[INFO]  Creating /usr/local/bin/ctr symlink to k3s
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
[INFO]  systemd: Starting k3s

Notice that K3s creates symlinks for kubectl, crictl, and ctr automatically. No separate kubectl install needed.

Verify the K3s Installation

Confirm the service is active:

systemctl status k3s

The output should show active (running):

● k3s.service - Lightweight Kubernetes
     Loaded: loaded (/etc/systemd/system/k3s.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-04-14 09:10:54 UTC; 1min 17s ago
       Docs: https://k3s.io
   Main PID: 1955 (k3s-server)
      Tasks: 79
     Memory: 1.5G (peak: 1.5G)
        CPU: 37.500s
     CGroup: /system.slice/k3s.service
             ├─1955 "/usr/local/bin/k3s server"
             ├─1977 "containerd "

Check the installed version:

k3s --version

You should see the K3s version along with the Go runtime:

k3s version v1.34.6+k3s1 (234e6132)
go version go1.24.13

Verify the node is ready and check the Kubernetes version reported by the API server:

kubectl get nodes -o wide

The node shows Ready status with containerd as the container runtime:

NAME        STATUS   ROLES           AGE   VERSION        INTERNAL-IP     EXTERNAL-IP   OS-IMAGE         KERNEL-VERSION     CONTAINER-RUNTIME
u2604-k3s   Ready    control-plane   3m    v1.34.6+k3s1   10.0.1.50       <none>        Ubuntu 26.04     7.0.0-10-generic   containerd://2.2.2-bd1.34

K3s bundles its own containerd runtime, so you do not need to install Docker or Docker Compose separately. The containerd instance K3s ships is independent of any system-level Docker installation.

Check System Pods

K3s deploys several essential components out of the box. List all pods across namespaces:

kubectl get pods -A

All system pods should show Running or Completed status:

NAMESPACE     NAME                                      READY   STATUS      RESTARTS   AGE
kube-system   coredns-76c974cb66-rmgnb                  1/1     Running     0          3m
kube-system   helm-install-traefik-crd-jz2hz            0/1     Completed   0          3m
kube-system   helm-install-traefik-sr6kp                0/1     Completed   1          3m
kube-system   local-path-provisioner-8686667995-vvhl8   1/1     Running     0          3m
kube-system   metrics-server-c8774f4f4-tcbkx            1/1     Running     0          3m
kube-system   svclb-traefik-4c27f6a5-n29tz              2/2     Running     0          2m
kube-system   traefik-c5c8bf4ff-zcqhm                   1/1     Running     0          2m

Here is what each component does:

  • CoreDNS provides cluster DNS resolution
  • Traefik acts as the default ingress controller (v3.6.10 in this release)
  • metrics-server enables kubectl top for resource monitoring
  • local-path-provisioner provides dynamic PersistentVolume provisioning using host paths
  • svclb-traefik is the ServiceLB (formerly Klipper) that exposes Traefik on the host network

Deploy a Sample Application

Create an nginx deployment with two replicas to verify the cluster handles workloads correctly:

kubectl create deployment nginx --image=nginx:latest --replicas=2

Expose it as a ClusterIP service:

kubectl expose deployment nginx --port=80 --type=ClusterIP

Check the deployment status:

kubectl get deployments

Both replicas should be available:

NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   2/2     2            2           21s

Verify the pods are running with their cluster IPs:

kubectl get pods -o wide

Each pod gets an IP from the K3s Flannel CNI (10.42.0.0/16 range by default):

NAME                     READY   STATUS    RESTARTS   AGE   IP           NODE        NOMINATED NODE   READINESS GATES
nginx-7c5d8bf9f7-227fn   1/1     Running   0          21s   10.42.0.10   u2604-k3s   <none>           <none>
nginx-7c5d8bf9f7-xdljr   1/1     Running   0          21s   10.42.0.9    u2604-k3s   <none>           <none>

Confirm the service is reachable:

kubectl get svc

The nginx service gets a cluster IP from the 10.43.0.0/16 service CIDR:

NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.43.0.1       <none>        443/TCP   3m
nginx        ClusterIP   10.43.232.247   <none>        80/TCP    21s

Configure Traefik Ingress

K3s ships with Traefik as the default ingress controller, so there is nothing extra to install. Create an Ingress resource to route traffic to the nginx service:

kubectl apply -f - <<ENDINPUT
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
  namespace: default
spec:
  rules:
  - host: nginx.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx
            port:
              number: 80
ENDINPUT

Verify the ingress was created and assigned the node IP:

kubectl get ingress

Traefik picks up the ingress immediately:

NAME            CLASS     HOSTS         ADDRESS       PORTS   AGE
nginx-ingress   traefik   nginx.local   10.0.1.50     80      14s

Test it with curl using the Host header:

curl -s -H "Host: nginx.local" http://localhost/ | head -5

You should see the nginx welcome page HTML:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>

For production use, replace nginx.local with a real domain and add TLS termination via cert-manager or Traefik’s built-in ACME support.

Monitor Resource Usage

Since K3s includes metrics-server, kubectl top works without any additional setup. Check node resource consumption:

kubectl top nodes

K3s uses minimal resources compared to a full kubeadm cluster. For purely local development and testing, Minikube is another option that runs inside a Docker container:

NAME        CPU(cores)   CPU(%)   MEMORY(bytes)   MEMORY(%)
u2604-k3s   94m          4%       861Mi           22%

Check per-pod resource usage across all namespaces:

kubectl top pods -A

The entire K3s control plane plus Traefik ingress runs under 70 MB of memory:

NAMESPACE     NAME                                      CPU(cores)   MEMORY(bytes)
default       nginx-7c5d8bf9f7-227fn                    0m           3Mi
default       nginx-7c5d8bf9f7-xdljr                    0m           3Mi
kube-system   coredns-76c974cb66-rmgnb                  2m           12Mi
kube-system   local-path-provisioner-8686667995-vvhl8   1m           7Mi
kube-system   metrics-server-c8774f4f4-tcbkx            5m           18Mi
kube-system   svclb-traefik-4c27f6a5-n29tz              0m           0Mi
kube-system   traefik-c5c8bf4ff-zcqhm                   1m           20Mi

Install Helm and Deploy Charts

K3s uses its own kubeconfig at /etc/rancher/k3s/k3s.yaml. Helm needs the KUBECONFIG environment variable set to find it. Install Helm first, then configure the environment. For more details, see the Helm 3 installation guide.

curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

Helm v3.20.2 installs to /usr/local/bin/helm:

Downloading https://get.helm.sh/helm-v3.20.2-linux-amd64.tar.gz
Verifying checksum... Done.
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm

Set the KUBECONFIG variable so Helm can talk to the K3s API server. Add this to your shell profile for persistence:

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
echo 'export KUBECONFIG=/etc/rancher/k3s/k3s.yaml' >> ~/.bashrc

Verify Helm can reach the cluster:

helm version

The output confirms the Helm client version:

version.BuildInfo{Version:"v3.20.2", GitCommit:"8fb76d6ab555577e98e23b7500009537a471feee", GitTreeState:"clean", GoVersion:"go1.25.9"}

K3s already manages Traefik via Helm internally. List the existing Helm releases:

helm list -A

The Traefik chart was deployed automatically during K3s installation:

NAME       	NAMESPACE  	REVISION	UPDATED                               	STATUS  	CHART                        	APP VERSION
traefik    	kube-system	1       	2026-04-14 09:11:40.334248311 +0000 UTC	deployed	traefik-39.0.501+up39.0.5    	v3.6.10
traefik-crd	kube-system	1       	2026-04-14 09:11:37.076963201 +0000 UTC	deployed	traefik-crd-39.0.501+up39.0.5	v3.6.10

Add a chart repository and deploy a workload. This example deploys Redis from the Bitnami repository:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

Install Redis in standalone mode (suitable for single-node K3s):

helm install redis bitnami/redis --set architecture=standalone --set master.persistence.enabled=false

After a minute, the Redis pod should be running alongside your other workloads:

kubectl get pods

Output shows the Redis master pod alongside the nginx deployment:

NAME                     READY   STATUS    RESTARTS   AGE
nginx-7c5d8bf9f7-227fn   1/1     Running   0          2m
nginx-7c5d8bf9f7-xdljr   1/1     Running   0          2m
redis-master-0           1/1     Running   0          43s

Remove the test Redis deployment when done:

helm uninstall redis

Configure Remote kubectl Access

K3s stores its kubeconfig at /etc/rancher/k3s/k3s.yaml. To manage the cluster from your workstation, copy this file and update the server address.

On the K3s server, display the kubeconfig:

cat /etc/rancher/k3s/k3s.yaml

The file contains the cluster CA certificate, client certificate, and client key. Copy it to your local machine:

scp [email protected]:/etc/rancher/k3s/k3s.yaml ~/.kube/k3s-config

Edit the file and replace 127.0.0.1 with the server’s actual IP address:

sed -i 's/127.0.0.1/10.0.1.50/g' ~/.kube/k3s-config

Use it by setting the KUBECONFIG variable or merging it with your existing config:

export KUBECONFIG=~/.kube/k3s-config
kubectl get nodes

Port 6443 must be reachable from your workstation. If you run UFW on the K3s server, allow the API port:

sudo ufw allow 6443/tcp

High Availability with Embedded etcd

By default, K3s uses SQLite as its datastore. For production clusters that need fault tolerance, K3s supports embedded etcd. This eliminates the need for an external etcd cluster while still providing HA.

To initialize a new HA cluster, start the first server with the --cluster-init flag:

curl -sfL https://get.k3s.io | K3S_TOKEN=my-shared-secret sh -s - server --cluster-init

This switches the datastore from SQLite to embedded etcd. On additional server nodes, join the existing cluster:

curl -sfL https://get.k3s.io | K3S_TOKEN=my-shared-secret sh -s - server --server https://10.0.1.50:6443

You need at least three server nodes for a quorum. The K3S_TOKEN value must match across all nodes. After joining, verify all server nodes appear:

kubectl get nodes

All three nodes should show the control-plane role with Ready status. If one server goes down, the remaining two maintain quorum and the cluster continues operating.

Add Worker Nodes

K3s worker nodes (called agents) are lightweight. They run kubelet and the container runtime but no control plane components.

First, get the node token from the server:

cat /var/lib/rancher/k3s/server/node-token

On each worker node, run the install script with the server URL and token:

curl -sfL https://get.k3s.io | K3S_URL=https://10.0.1.50:6443 K3S_TOKEN=<node-token-here> sh -

The agent joins the cluster within seconds. Verify from the server:

kubectl get nodes

Worker nodes appear without the control-plane role, and the scheduler immediately starts placing pods on them.

cgroup v2 Compatibility

Ubuntu 26.04 runs cgroup v2 exclusively (unified hierarchy). K3s v1.34.x works with cgroup v2 out of the box with no additional configuration needed. You can verify the cgroup version on your system:

mount | grep cgroup

On Ubuntu 26.04, this shows the unified cgroup2 mount:

cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot,memory_hugetlb_accounting)

The available cgroup controllers on this kernel:

cat /sys/fs/cgroup/cgroup.controllers

Output confirms full cgroup v2 support including memory, CPU, and IO controllers:

cpuset cpu io memory hugetlb pids rdma misc dmem

Earlier versions of K3s (pre-v1.25) required manual cgroup configuration on Ubuntu. That is no longer the case. The containerd runtime bundled with K3s handles cgroup v2 natively.

Uninstall K3s

K3s provides a clean uninstall script that removes the binary, systemd services, and all cluster data. On the server node:

/usr/local/bin/k3s-uninstall.sh

On agent (worker) nodes, the script is slightly different:

/usr/local/bin/k3s-agent-uninstall.sh

These scripts stop the service, remove iptables rules, unmount K3s-related filesystems, and delete /var/lib/rancher/k3s. The uninstall is thorough, which is one of K3s’s advantages over kubeadm where cleanup often leaves residual state.

K3s vs kubeadm vs Minikube

Choosing the right Kubernetes distribution depends on your use case. Here is how K3s compares to kubeadm and Minikube based on real testing:

FeatureK3skubeadmMinikube
Install time~30 seconds5-15 minutes2-5 minutes
Binary size~70 MB (single binary)Multiple binaries + container images~90 MB + driver
Minimum RAM512 MB (server), 256 MB (agent)2 GB2 GB
Container runtimeBundled containerdExternal (containerd/CRI-O)Bundled Docker/containerd
Ingress controllerTraefik (built-in)None (install separately)Nginx (addon)
Metrics serverBuilt-inInstall separatelyAddon
HA supportEmbedded etcd or external DBExternal etcd or stackedNot supported
CNIFlannel (default)User choice (Calico, Cilium, etc.)Bundled
Best forEdge, IoT, CI/CD, lightweight prodFull-featured production clustersLocal development only
Multi-nodeYes (server + agents)YesLimited
CNCF certifiedYesYesYes
UninstallClean (one script)Manual (kubeadm reset + cleanup)minikube delete

K3s trades some flexibility (you cannot swap the CNI or scheduler as easily) for simplicity. If you need Calico or Cilium as your CNI, or if you want to run a multi-tenant production cluster with fine-grained RBAC, kubeadm gives you that control. For everything else, K3s gets you a working cluster faster with fewer moving parts.

K3s cluster running on Ubuntu 26.04 showing version, nodes, and pods
K3s v1.34.6 cluster on Ubuntu 26.04 LTS with running pods

Related Articles

Automation Automate Deployments Using Docker and Terraform Networking Install Neat IP Address Planner(NIPAP) on Ubuntu/Debian Monitoring How To Install Grafana on Debian 12/11/10 Databases How To Install LAMP Stack on Ubuntu 24.04 (Noble Numbat)

Leave a Comment

Press ESC to close