MicroK8s is a lightweight, conformant Kubernetes distribution from Canonical, delivered as a single snap package. Extra components such as ingress, storage, a container registry or observability are switched on as addons with one command. In this tutorial you will install MicroK8s on Ubuntu 24.04, use it without sudo, enable the addons most workloads need, deploy a test application behind the ingress controller and, optionally, join more nodes to form a cluster.
Prerequisites
To follow this tutorial, you will need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 2 vCPU, 4 GB of RAM and 20 GB of free disk space.
- A non-root user with
sudoprivileges. snapd, which is installed by default on Ubuntu 24.04.- Optionally, one or two more Ubuntu 24.04 servers if you want to build a multi-node cluster in Step 6.
Step 1 - Installing MicroK8s
MicroK8s publishes one snap channel per Kubernetes minor version. List the available channels to see which versions you can install:
snap info microk8s | sed -n '/channels:/,$p' | head -n 12
Install MicroK8s from the default stable channel. The --classic flag is required because MicroK8s needs full access to the host to run containers:
sudo snap install microk8s --classic
microk8s (1.xx/stable) v1.xx.x from Canonical✓ installed
NoteTo pin a Kubernetes minor version, pass a channel, for example
sudo snap install microk8s --classic --channel=1.33/stable. Snap refreshes stay within that minor version.
Wait until all core services report ready:
sudo microk8s status --wait-ready
microk8s is running
high-availability: no
datastore master nodes: 127.0.0.1:19001
datastore standby nodes: none
addons:
enabled:
dns # (core) CoreDNS
ha-cluster # (core) Configure high availability on the current node
helm # (core) Helm - the package manager for Kubernetes
helm3 # (core) Helm 3 - the package manager for Kubernetes
disabled:
...
Recent releases enable the dns addon by default. If it shows under disabled, you will enable it in Step 3.
Step 2 - Running MicroK8s without sudo
MicroK8s creates a microk8s group whose members can run its commands without sudo. Add your user to it and create the directory that kubectl uses for its cache:
sudo usermod -a -G microk8s "$USER"
mkdir -p ~/.kube
chmod 0700 ~/.kube
Group changes apply to new login sessions. Start a shell with the new group, or log out and back in:
newgrp microk8s
Verify access by listing the node:
microk8s kubectl get nodes
NAME STATUS ROLES AGE VERSION
microk8s1 Ready <none> 3m v1.xx.x
Typing microk8s kubectl gets tedious. If you have no other kubectl installed, create a snap alias so kubectl points to the bundled client:
sudo snap alias microk8s.kubectl kubectl
The rest of this tutorial uses kubectl.
Step 3 - Enabling the core addons
A fresh MicroK8s node has DNS but no storage provisioner or ingress controller. Enable the three addons most applications rely on:
microk8s enable dns
microk8s enable hostpath-storage
microk8s enable ingress
dnsdeploys CoreDNS so pods can resolve Services by name.hostpath-storagecreates the default StorageClassmicrok8s-hostpath, which stores volumes in a directory on the node.ingressdeploys the NGINX ingress controller, which listens on ports 80 and 443 of every node.
Check that the addons are running:
kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
ingress nginx-ingress-microk8s-controller-xxxxx 1/1 Running 0 60s
kube-system calico-kube-controllers-xxxxxxxxxx-xxxxx 1/1 Running 0 6m
kube-system calico-node-xxxxx 1/1 Running 0 6m
kube-system coredns-xxxxxxxxxx-xxxxx 1/1 Running 0 6m
kube-system hostpath-provisioner-xxxxxxxxxx-xxxxx 1/1 Running 0 90s
Confirm the StorageClass exists:
kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
microk8s-hostpath (default) microk8s.io/hostpath Delete WaitForFirstConsumer false 2m
Run microk8s status at any time to see the full list of enabled and available addons. Other useful ones are metrics-server (enables kubectl top), registry (a private image registry on port 32000), cert-manager and observability (Prometheus, Grafana and Loki).
Warning
hostpath-storagekeeps data on a single node and is meant for single-node clusters and testing. On a multi-node cluster, use a replicated storage addon or an external storage solution for production data.
Step 4 - Allowing traffic through the firewall
If UFW is enabled, MicroK8s pods need routed traffic to be allowed, and the ingress controller needs HTTP and HTTPS:
sudo ufw allow OpenSSH
sudo ufw default allow routed
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
If you want to use kubectl from your workstation, also allow the API server port, ideally only from your own IP. Replace your_admin_ip with it:
sudo ufw allow from your_admin_ip to any port 16443 proto tcp
Verify the rules:
sudo ufw status verbose
Status: active
Default: deny (incoming), allow (outgoing), allow (routed)
...
Step 5 - Deploying a test application
Deploy Nginx with two replicas, expose it as a Service and publish it through the ingress controller. This checks scheduling, cluster DNS, Service networking and ingress in one go.
kubectl create deployment hello --image=nginx:stable --replicas=2
kubectl expose deployment hello --port=80
Create the Ingress manifest:
nano hello-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello
spec:
ingressClassName: public
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello
port:
number: 80
MicroK8s registers its NGINX controller under the IngressClass public (with nginx as an alias class). Check the class name on your install and apply the manifest:
kubectl get ingressclass
kubectl apply -f hello-ingress.yaml
Test it from any machine, replacing your_server_ip with the public IP of the node:
curl -I http://your_server_ip
HTTP/1.1 200 OK
Content-Type: text/html
...
To test persistent storage, create a PersistentVolumeClaim:
nano data-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
kubectl apply -f data-pvc.yaml
kubectl get pvc data
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data Pending microk8s-hostpath 5s
Pending is expected when the StorageClass uses the WaitForFirstConsumer binding mode shown in Step 3: the volume is created only when a pod first uses the claim, and the status then changes to Bound.
Clean up the test resources:
kubectl delete -f hello-ingress.yaml -f data-pvc.yaml
kubectl delete service,deployment hello
Step 6 - Joining additional nodes (optional)
MicroK8s forms a cluster with a join token generated on an existing node. With three or more full nodes, the ha-cluster addon automatically replicates the datastore for high availability.
First, install MicroK8s on each new node as in Step 1, using the same channel. Between the nodes, allow the cluster ports. Replace node_ip with the IP of each peer node and run these on every node:
sudo ufw allow from node_ip to any port 16443 proto tcp
sudo ufw allow from node_ip to any port 25000 proto tcp
sudo ufw allow from node_ip to any port 19001 proto tcp
sudo ufw allow from node_ip to any port 10250 proto tcp
sudo ufw allow from node_ip to any port 4789 proto udp
On the first node, generate a join command:
microk8s add-node
From the node you wish to join to this cluster, run the following:
microk8s join 203.0.113.10:25000/2f1c3e.../a8b9c0...
Use the '--worker' flag to join a node as a worker not running the control plane, eg:
microk8s join 203.0.113.10:25000/2f1c3e.../a8b9c0... --worker
...
Each token is single use. On the new node, run the printed command. Add --worker if the node should only run workloads:
sudo microk8s join 203.0.113.10:25000/2f1c3e.../a8b9c0... --worker
Back on the first node, confirm the new node is Ready:
kubectl get nodes
NAME STATUS ROLES AGE VERSION
microk8s1 Ready <none> 40m v1.xx.x
microk8s2 Ready <none> 50s v1.xx.x
To remove a node later, run sudo microk8s leave on that node and then microk8s remove-node node_name on a remaining node.
Step 7 - Accessing the cluster from your workstation
MicroK8s can print a kubeconfig with admin credentials for the cluster:
microk8s config > microk8s-kubeconfig
Copy the file to your workstation (for example with scp), make sure its server: line points to https://your_server_ip:16443, and use it:
KUBECONFIG=./microk8s-kubeconfig kubectl get nodes
ImportantThis kubeconfig grants full cluster-admin access. Store it with
chmod 600and never commit it to a repository.
Troubleshooting
microk8s status says it is not running. Run sudo microk8s inspect. It checks the services, collects logs into a tarball and prints warnings such as missing IP forwarding or firewall issues.
Insufficient permissions to access MicroK8s. Your shell does not have the microk8s group yet. Run newgrp microk8s or log in again.
The ingress returns connection refused. Check that the ingress pod is Running in the ingress namespace and that ports 80 and 443 are allowed in UFW and in any provider firewall.
Pods cannot resolve DNS names. Make sure the dns addon is enabled and that sudo ufw default allow routed is set.
Removing MicroK8s. sudo snap remove microk8s --purge removes the snap and all cluster data.
Conclusion
You installed MicroK8s on Ubuntu 24.04, configured non-root access, enabled DNS, storage and ingress, verified them with a test application and learned how to grow the node into a cluster. Next, you can enable the cert-manager addon to issue TLS certificates for your ingresses, enable observability for metrics and logs, or deploy applications with the bundled Helm using microk8s helm.
