How to set up Mainframe in Raspberry Pi
In this article, we will see how to install Multiple Virtual Storage, more commonly called MVS operating system into …
In this article, we will see how to install and configure a Kubernetes cluster on a Raspberry Pi. Docker, Ansible, k3s, and the Raspberry Pi 4 (8GB) were used for this setup. K3s is a fully compliant, lightweight Kubernetes distribution.
List of required hardware components:
| Hardware Type | Model |
|---|---|
| Raspberry Pi | Raspberry Pi 4 (8 GB) |
| Case | C4Labs Cloudlet Cluster Case |
| Memory Card | SanDisk 128GB Ultra MicroSDXC |
| Power Supply | Anker PowerPort 6 (60W 6-Port USB Charging Hub) |
| USB Cable | 1 Feet Cabepow USB A to Type C Cable |
| Cat6 Cable | Rankie RJ45 Cat6 |
| Wireless Router | TP-Link Wireless N Nano Router |
| Network Switch | TP-Link 5 Port Gigabit Ethernet Network Switch |
Unbox the Raspberry Pi and all accessories
Attach the fan to the case
Install heat sinks on the CPU, RAM, and USB controller chip
Install the fan by connecting the RED wire to the second pin and the BLACK wire to the third pin in the top row from the left side
Safely attach the Raspberry Pi to the case and connect the power supply and ethernet cables
Attach the network switch, router, and power supply to the case using double-sided tape (optional). If you are not using a wireless router, you can also connect the Pis directly to the router using Cat 6 cables
Download and install Etcher on your machine
Download the Ubuntu image for Raspberry Pi. Pick the OS type you need. We will use Ubuntu Server for this setup, but you can also use Ubuntu Desktop if you have a micro HDMI cable and a USB keyboard
Insert the SD card into your machine and flash it using Etcher to install Ubuntu for Raspberry Pi
For Ubuntu Server, enable SSH by adding an empty file named ssh inside the “system-boot” directory of the SD card. This lets us bring up the Raspberry Pi completely headless
For Ubuntu Desktop, connect the Pi to a display and keyboard, then enable SSH using the following method
sudo apt update
sudo apt upgrade
sudo apt install openssh-server
sudo service ssh enable
sudo service ssh start
Insert the SD card into each Raspberry Pi’s SD slot and power on the devices
Identify the IP address of all Raspberry Pis using the nmap command, or from the router’s admin console. It’s advisable to reserve the IP address for each Raspberry Pi in the router settings so the IP addresses won’t change with future restarts
# Adjust the IP address to the default gateway address of your network
sudo nmap -sn 192.168.0.1/24
SSH into each Raspberry Pi using the credential ubuntu/ubuntu and change the default credential to a more secure one
apt update && apt upgrade and plan on reinstalling k3s (see Install Kubernetes) rather than trying to revive the existing installation.We will use the official Ansible template from k3s, plus a few additional Ansible playbooks, to install and configure k3s.
🔗 Ansible Playbook - k3s 🔗 Ansible Playbook - Pi Config
k3s-ansible has restructured since this post was first written — it’s now an Ansible collection using inventory.yml with server/agent groups, instead of the old hosts.ini with master/node. The steps below use the current structure and have been tested end-to-end against a clean cluster.Clone both repositories side by side into a single parent directory, and keep a $REPOS variable pointing at it — every cd command in the rest of this post uses it, so re-export it at the start of each new kube-tools shell:
export REPOS=/path/to/where/you/keep/repos # must be on the host mount kube-tools bind-mounts (check its docker-compose.yml volumes) - not the container's own home directory, which is wiped if kube-tools is ever recreated
mkdir -p $REPOS && cd $REPOS
git clone https://github.com/k3s-io/k3s-ansible
git clone https://github.com/entechlog/kubernetes-examples
Download and install Docker for your platform. Click here for instructions
Open a new terminal and cd into the kubernetes-examples/kube-tools directory. This directory contains a Dockerfile and docker-compose.yml to bring up a Docker container with Ansible.
The kube-tools image was originally based on python:3.9-slim-buster. Debian Buster’s LTS support ended June 2024 and its apt repositories have been taken offline, so the image will fail to build (apt-get update returns 404s). It’s been updated to python:3.13-slim-trixie (Debian 13, the current stable release — note Debian 12 “Bookworm” moved to LTS-only maintenance in July 2026, so it’s still usable but no longer current). The Dockerfile also needed fixes for a few other repos/URLs that have since changed:
apt.kubernetes.io to pkgs.k8s.io (the legacy repo was fully shut down March 2024) — also bump the pinned version path (core:/stable:/v1.30/) to whatever the current maintained minor version is; Kubernetes only maintains the latest 3 minor releases.https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3).k9s_Linux_x86_64.tar.gz → k9s_Linux_amd64.tar.gz) somewhere between versions, and the octant install step hardcoded its .deb filename instead of using ${OCTANT_VERSION}, silently breaking if that version is ever bumped. Also worth knowing: Octant itself was archived by VMware in January 2023 and is no longer maintained — the repo lives on as a read-only vmware-archive/octant.Create a copy of .env.template as .env
.envStart the kube-tools container by running
docker compose up -d
Validate the container by running
docker ps
Shell into the container by running
docker exec -it kube-tools /bin/bash
Validate Ansible by running
ansible --version
cd into the cloned Pi-config repository
cd $REPOS/kubernetes-examples/rpi-k3/configure/
Edit hosts.ini with the IP addresses gathered above, and a user-friendly hostname you’d like to set for each Raspberry Pi, in the format below.
[master]
pi-kube-m1 ansible_ssh_host=192.168.0.100
[node]
pi-kube-n1 ansible_ssh_host=192.168.0.101
pi-kube-n2 ansible_ssh_host=192.168.0.102
[k3s_cluster:children]
master
node
cd into the cloned k3s-ansible repository
cd $REPOS/k3s-ansible/
Install the collection’s Ansible dependencies
ansible-galaxy collection install -r collections/requirements.yml
kube-tools container on Windows, Ansible silently ignores this repo’s own ansible.cfg (which sets roles_path) because the bind-mounted host drive looks “world writable” to Linux — you’ll hit the role 'prereq' was not found. Work around it by setting the roles path explicitly before running any playbook here: export ANSIBLE_ROLES_PATH=$(pwd)/rolesCopy the sample inventory to inventory.yml
cp inventory-sample.yml inventory.yml
Edit inventory.yml with the IP addresses gathered above, in the following format. Generate a value for token with openssl rand -base64 64 — this is the shared secret nodes use to join the cluster, so don’t reuse the example below, and don’t commit it anywhere public.
k3s_cluster:
children:
server:
hosts:
192.168.0.100:
agent:
hosts:
192.168.0.101:
192.168.0.102:
vars:
ansible_user: ubuntu
k3s_version: v1.36.3+k3s1
token: "<output of openssl rand -base64 64>"
api_endpoint: "{{ hostvars[groups['server'][0]]['ansible_host'] | default(groups['server'][0]) }}"
Check the k3s releases page for a current stable version to use instead of the one shown above.
cd into the cloned Pi-config repository
cd $REPOS/kubernetes-examples/
Generate an RSA key by running the following playbook
ansible-playbook rpi-k3/configure/01-generate-rsa.yml
⚠️ Alternatively, the RSA key can be generated manually by running the following commands ⚠️
ssh-keygen -t rsa
cat /home/ubuntu/.ssh/id_rsa.pub > ~/.ssh/authorized_keys
Copy the RSA key to all Raspberry Pis by running the following playbook
export ANSIBLE_HOST_KEY_CHECKING=False
ansible-playbook rpi-k3/configure/02-copy-rsa.yml -i rpi-k3/configure/hosts.ini --ask-pass
export ANSIBLE_HOST_KEY_CHECKING=True
⚠️ Alternatively, the RSA key can be copied manually by running the following command for each Pi ⚠️
cat /home/ubuntu/.ssh/id_rsa.pub | ssh ubuntu@192.168.0.100 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
Update the hostnames of the Raspberry Pis by running the playbook below
ansible-playbook -e 'reboot=True' \
rpi-k3/configure/03-change-hostname.yml -i rpi-k3/configure/hosts.ini
⚠️ Alternatively, the hostname can also be changed manually by SSHing into each Pi and editing the hostname in /etc/hostname and /etc/hosts, then issuing a reboot command ⚠️
ansible-playbook rpi-k3/configure/99-install-python.yml -i rpi-k3/configure/hosts.iniFailCgroupV1 defaults to true) and the kubelet refuses to start without it, but k3s-ansible’s Raspberry Pi role still only enables the legacy cgroup v1 flags as of this writing. Rather than hand-patching files inside the cloned collection (fragile, and easy to lose track of on a re-clone), this is a small standalone playbook that only touches the boot commandline.cd into the cloned Pi-config repository
cd $REPOS/kubernetes-examples/
Run the following playbook to enable cgroup v2 and reboot the Pis
ansible-playbook rpi-k3/configure/06-fix-cgroup-v2.yml -i rpi-k3/configure/hosts.ini
cd into the cloned k3s-ansible repository
cd $REPOS/k3s-ansible/
Every ansible-playbook command from here needs a private key, disabled host-key checking, and (per the warning above) an explicit roles path — set all three once per shell session:
export ANSIBLE_PRIVATE_KEY_FILE=/path/to/your/id_rsa
export ANSIBLE_HOST_KEY_CHECKING=False
export ANSIBLE_ROLES_PATH=$(pwd)/roles
Start provisioning the cluster using the following command
ansible-playbook playbooks/site.yml -i inventory.yml
Get the kubeconfig from the master and set KUBECONFIG. Save it under kubernetes-examples/kube-tools/.kube/ on the mounted drive, not ~/.kube/config — the latter is container-local and gets wiped if kube-tools is ever recreated:
mkdir -p $REPOS/kubernetes-examples/kube-tools/.kube
scp ubuntu@192.168.0.100:~/.kube/config $REPOS/kubernetes-examples/kube-tools/.kube/config
export KUBECONFIG=$REPOS/kubernetes-examples/kube-tools/.kube/config
Unlike the older role, the current collection doesn’t rewrite the kubeconfig’s server address for you — it’ll point at 127.0.0.1, which only works from the master itself. Fix it before using kubectl from your own machine:
kubectl config set-cluster default --server=https://192.168.0.100:6443 --kubeconfig $KUBECONFIG
Validate the Kubernetes nodes by running
kubectl get nodes
All nodes showing Ready doesn’t guarantee the pod network between them actually works. k3s-agent nodes can come up with an incomplete Flannel mesh — each agent only learns the server’s pod route during a race at initial join, not the other agents’ — so cross-node pod traffic (e.g. a pod on node A reaching a pod on node B) can silently time out while the install itself reports success. Run this once, back in kubernetes-examples/ (not k3s-ansible/), to force Flannel to redo peer discovery and confirm the mesh is actually complete:
cd $REPOS/kubernetes-examples/
ansible-playbook rpi-k3/configure/07-verify-cluster-network.yml -i rpi-k3/configure/hosts.ini
It fails loudly if any node is still missing routes, rather than leaving you to debug a mysterious connection timeout later (this is exactly how the issue first showed up — as a Grafana panel unable to reach Prometheus).
If you want to reset the Kubernetes cluster, you can do so by running the following command first, then starting over from Install Kubernetes
ansible-playbook playbooks/reset.yml -i inventory.yml
A cluster with nothing running on it doesn’t prove much, so let’s put it to real use: monitoring itself. kube-prometheus-stack bundles Prometheus, Grafana, Alertmanager, node-exporter, and kube-state-metrics into one Helm chart, giving you node and pod health over time rather than just a point-in-time snapshot.
rpi-k3/monitoring/values.yaml trims requests/limits down to a footprint that fits comfortably on a 3-node Raspberry Pi 4 (8GB) cluster, and extends Grafana’s startup probes — first boot on a Pi is slow, since Grafana downloads a handful of default app plugins over the network before it starts serving, and the chart’s default probe timing kills the pod before that finishes.Install the monitoring stack — inside kube-tools, with KUBECONFIG still exported from the previous section:
cd $REPOS/kubernetes-examples/
bash rpi-k3/monitoring/install-monitoring.sh
This adds the Helm repo, installs the chart with the trimmed values.yaml, and waits for all pods to be ready. First install can take several minutes (image pulls + Grafana’s plugin downloads); later runs are much faster since images are cached.
cd’d into place inside a kube-tools shell), not as docker exec kube-tools bash rpi-k3/monitoring/install-monitoring.sh from your host terminal — docker exec resolves relative paths against the container’s own default directory, not wherever your host shell happens to be, so a bare relative path there fails with “No such file or directory”.Open Grafana — run this on your own machine, not inside a container, cd’d into the same kubernetes-examples/ directory (on the host path, not $REPOS — that variable only exists inside the kube-tools shell where you exported it)
bash rpi-k3/monitoring/open-monitoring.sh
This fetches the Grafana admin password, opens an SSH tunnel, and launches your browser at http://localhost:3000 once the tunnel is up. Log in as admin with the printed password.
See <code>rpi-k3/monitoring/README.md</code> for more detail, including a known kubectl top/metrics-server issue on this setup that’s unrelated to the monitoring stack itself (Prometheus scrapes node-exporter and kube-state-metrics directly, independent of the Kubernetes metrics-server API).
Kubernetes Dashboard is a general-purpose, web-based UI for browsing and editing cluster resources — useful for ad-hoc debugging, but not something you need running continuously alongside the monitoring stack above, and its multi-pod footprint (api, auth, web, metrics-scraper, and a Kong gateway) is worth the reserved capacity only if you’ll actually use it. See <code>rpi-k3/dashboard/README.md</code> for install/access instructions using the same install/open script pattern as monitoring.
Now we have a Kubernetes cluster running on Raspberry Pis, actively monitoring itself and ready to run more workloads.
Inside kube-tools (docker exec -it kube-tools /bin/bash), with KUBECONFIG exported as in Install Kubernetes (re-export it if this is a fresh shell). Run the command below to remove the monitoring stack
helm uninstall kube-prometheus-stack -n monitoring
kubectl delete namespace monitoring
If you also installed the optional dashboard, see <code>rpi-k3/dashboard/README.md</code> for its removal steps.
If you want to wipe k3s off the Raspberry Pis entirely — not just the workloads on top — cd into the cloned k3s-ansible repository and run the reset playbook against all three nodes. This is the same command used to test this post’s instructions from a clean state, so it’s verified to actually leave the Pis ready for a fresh Install Kubernetes run.
If this is a fresh kube-tools shell, re-export the same variables from Install Kubernetes first — without them the reset playbook fails the same way the install one does (Permission denied or the role 'prereq' was not found):
export REPOS=/path/to/where/you/keep/repos
cd $REPOS/k3s-ansible/
export ANSIBLE_PRIVATE_KEY_FILE=/path/to/your/id_rsa
export ANSIBLE_HOST_KEY_CHECKING=False
export ANSIBLE_ROLES_PATH=$(pwd)/roles
ansible-playbook playbooks/reset.yml -i inventory.yml
This removes the k3s binaries, systemd services, and all cluster data (/etc/rancher/k3s, /var/lib/rancher/k3s, /var/lib/kubelet) from every node — the Pis themselves, hostnames, and SSH access are untouched.
Hope this was helpful. Did I miss something? Let me know in the comments.
In this article, we will see how to install Multiple Virtual Storage, more commonly called MVS operating system into …
The Raspberry Pi is a low cost, credit-card sized computer that plugs into a computer monitor or TV, and uses a standard …