Before you clone the repo and run the script, it helps to understand what you’re actually building and why each piece exists. This post is the conceptual overview of the UTM Simple cluster — what the 6 VMs do, what Kubernetes concepts they map to, and why UTM is the right choice when production realism matters most.
When you’re ready to deploy, the step-by-step hands-on guide takes you from zero to a running cluster in under 6 minutes. For help choosing between UTM, Vagrant, and OrbStack, see the full tool comparison.
The Architecture — 6 VMs, 5 Roles
The Simple cluster builds a complete Kubernetes environment across 6 virtual machines on your Mac. Each VM runs its own Ubuntu 24.04 kernel with genuine process isolation — the closest you can get to production cloud VMs without leaving your laptop. Here’s what each role does and why it exists.
Vault (PKI and Secrets). HashiCorp Vault manages all the TLS certificates your cluster needs. Instead of generating certificates with openssl and scattering them across nodes manually, Vault provides a proper Certificate Authority with a 3-tier hierarchy: Root CA → Intermediate CA → leaf CAs for Kubernetes, etcd, and front-proxy. This separation means a compromised etcd certificate can’t authenticate to the Kubernetes API. It’s more work than self-signed certs, but it mirrors how production environments handle PKI. The Vault PKI deep dive covers the full hierarchy.
Jump (Bastion and Ansible Controller). Your Mac connects only to the jump server — every other VM is accessed through it via SSH. This is the bastion server pattern used in production environments: instead of exposing every node directly, a single hardened entry point controls access. Jump also runs Ansible (which deploys everything) and hosts the kubectl config, so it’s your command center for the entire cluster.
etcd (Cluster State Store). Every piece of cluster state — deployments, services, secrets, config maps — lives in etcd. It’s a distributed key-value store, though in the Simple setup it runs as a single node. This makes it a single point of failure on purpose: if you stop etcd, the API server can’t read or write anything. That direct experience is what motivates the HA upgrade to a 3-node etcd cluster with quorum-based consensus.
Master (Control Plane). The master node runs the three core Kubernetes components as systemd services: kube-apiserver (the front door for all cluster operations), kube-controller-manager (reconciles desired state with actual state), and kube-scheduler (assigns pods to worker nodes). In the Simple setup there’s one master — if it goes down, you can’t deploy or modify anything, but existing workloads on workers keep running. The HA version adds a second master behind an HAProxy load balancer.
Workers (Data Plane). Two worker nodes run your actual workloads. Each worker runs containerd (the container runtime), kubelet (the node agent that registers with the API server), and kube-proxy (manages iptables rules for Kubernetes Services). Calico CNI handles pod-to-pod networking across both workers using the 10.244.0.0/16 CIDR.
What You’ll Actually Learn
This isn’t just “install Kubernetes.” Building the cluster the hard way — from individual binaries with no kubeadm — forces you to understand how each piece connects. Here’s what becomes concrete instead of abstract.
How certificates flow between components. The API server needs a server certificate (so clients trust it), a client certificate for etcd (so etcd trusts it), and a separate CA for the front-proxy aggregation layer. Each certificate has specific SANs, key usages, and TTLs. You’ll see exactly which cert goes where and why.
What kubelet actually does. It’s not magic — it’s a systemd service with a config file that points to the API server, a client certificate for authentication, and a set of flags that control everything from pod CIDR allocation to container runtime socket paths. Reading the kubelet unit file teaches more than any documentation page.
Why etcd is the most important component. Stop etcd and everything stops. Not because pods crash — they keep running — but because no new state can be written or read. Deployments hang, services can’t be created, and kubectl returns errors. This single experiment builds the operational intuition that etcd is the real heart of the cluster, not the API server.
How cloud-init provisions VMs. UTM uses the NoCloud datasource — the same mechanism AWS, GCP, and Azure use for instance provisioning. The deploy script generates cloud-init ISOs with meta-data, user-data, and network-config files for each VM. This is directly transferable knowledge for cloud infrastructure work.
Why UTM Specifically
All three tools — UTM, Vagrant, and OrbStack — produce the same functional cluster with the same Ansible roles. The difference is what you learn alongside Kubernetes and how closely it resembles production.
UTM creates full QEMU-backed VMs with their own kernels. Each VM boots Ubuntu 24.04 independently, gets its own disk image, and has a single network interface with one IP on UTM’s shared network bridge. No dual-NIC confusion, no shared-kernel shortcuts. When you configure a bind address in the API server’s systemd unit, there’s exactly one IP to choose — the same simplicity you’d find on an EC2 instance.
UTM also gives you cloud-init ISO provisioning, which Vagrant and OrbStack don’t use. If you plan to work with cloud infrastructure — Terraform, Packer, cloud-init templates — this is directly relevant practice.
The tradeoff is resource consumption. UTM pre-allocates RAM per VM (~26 GB total for the Simple cluster) and disk space (~170 GB). Your Mac runs warmer than with OrbStack’s shared-kernel approach. If resources are tight, the same architecture is available with OrbStack (lightest footprint) or Vagrant (declarative infrastructure-as-code).
What the Simple Cluster Deliberately Skips
Understanding what’s missing is just as important as understanding what’s included. The Simple setup has intentional single points of failure that the HA version addresses.
No HAProxy load balancer. Workers connect directly to master-1’s API server on port 6443. If that master goes down, the control plane is offline. The HA version puts two masters behind HAProxy for automatic failover.
Single etcd node. No quorum, no consensus protocol, no fault tolerance. One node failure means total data loss. The HA version runs 3 etcd nodes — any one can fail and the cluster keeps running because 2 out of 3 still form a majority.
One master. If the control plane fails, you can’t deploy, scale, or modify anything. Existing pods survive (they’re on workers), but the cluster is operationally dead until the master recovers.
These gaps are features, not bugs. They let you experience exactly what fails and why — the direct motivation for every HA component you’ll add later.
Who Should Start Here
The UTM Simple cluster is the right starting point if you want production-realistic VMs with full kernel isolation, plan to work with cloud infrastructure (cloud-init, Terraform, Packer), prefer the cleanest networking setup (single NIC per VM), and have a Mac with 32 GB+ RAM and disk space to spare.
If resource efficiency is your priority, start with the OrbStack Simple cluster instead — same architecture, fraction of the resources. If declarative infrastructure-as-code appeals to you, the Vagrant Simple cluster puts everything in a version-controlled Vagrantfile.
What’s Next
Ready to build? The UTM Simple hands-on guide walks through deployment from clone to kubectl get nodes. Once you’ve explored the cluster, broken services intentionally, and read the Ansible playbooks, the Learning Path maps the full progression from Simple to HA.
The HA upgrade adds 5 more VMs and eliminates every single point of failure — the UTM HA deep dive covers the full 17-step deployment.
Big tech, small lab. One reel at a time.
Questions, corrections, or want to share how you’re using these repos?
labitlearnit@gmail.com
Leave a Reply