Development environment
No inbound firewall ports open.
- Smallest viable: 1 control plane + 1 worker + 1 load balancer. Add two control planes and one load balancer later for resilience.
- VIP: Configure a VIP for the API server.
- etcd: Stacked etcd on the control-plane node.
- Environment: Virtualised using Parallels Desktop on Mac.
- OS: Ubuntu LTS, static IPs, NTP on, swap off.
- Bootstrap: Kubespray for Kubernetes deployment.
- CNI: Calico.
- Load balancer: MetalLB in L3 BGP (FRR) mode connected to local router.
- Ingress: Ingress-NGINX.
- Storage: NFS subdir provisioner connected to NAS.
- Backups: Velero to S3-compatible buckets connected to NAS.
- TLS: cert-manager. Use a wildcard certificate for ingress hostnames (manually installed).
Let’s Encrypt’s HTTP-01 challenge requires exposing port 80 to the Internet. For on-premises, prefer DNS-01 so you don’t open inbound ports and you can issue wildcards.
Terminology summary
- Free. No cost to use, but not necessarily open to modify (for example, a Parallels trial, or other proprietary software).
- Open-source. Freely available and source code accessible for modification and redistribution (for example, Ubuntu, Kubernetes, Kubespray).
- Commercial. Paid or licensed product, usually proprietary (for example, macOS, Parallels).
Start virtualised and small
Start with a small, fully virtualised development environment on a single workstation, in my case a 16 GB MacBook Air (M3).
For day-to-day development I use Parallels virtual machines (VMs). I also test hybrid and bare metal with Kubespray in separate setups.
If you need a quick comparison of host platforms and bootstrap options, see the tables on the Environment Types page.
Goals
- Keep macOS responsive while running a single load balancer, a control-plane node, and a worker node (and optionally an Ansible controller).
- Run Kubernetes Dashboard, Metrics Server, and lightweight applications.
Development environment
| # | Tool | Version | Type or licence | Notes |
|---|---|---|---|---|
| 1 | MacBook Air M3 (15-inch, 16 GB RAM, 500 GB SSD) | macOS 15.6.1 | Commercial hardware and OS (Apple proprietary). | Primary host system. |
| 2 | Parallels Desktop Pro | 26.0.1 | Commercial (subscription). | Used for running Ubuntu VMs. |
| 3 | High Speed Internet | – | 1Gbps Fibre to the Premises. | Provides Internet connectivity. |
| 4 | Router/Firewall/Switch | – | Consumer hardware or proprietary firmware. | Provides LAN and Internet connectivity. |
| 5 | Ubuntu Server LTS | 24.04.3 LTS | Free and open-source (Canonical Ltd., GPL). | Base operating system for all nodes. |
| 6 | Kubernetes | 1.32.9 | Free and open-source (CNCF, Apache 2.0). | Cluster orchestration platform. |
| 7 | Kubespray | 2.29.0 | Free and open-source (Apache 2.0). | Ansible-based Kubernetes deployment tool. |
Notes
- Keep versions pinned and record them in the repo for reproducibility.
- Use the same Kubernetes minor version across dev and prod where possible.
- Prefer one container network interface and one ingress across environments.
Virtual machines
The virtual machine build instructions can be found on the Provisioning Kubernetes Clusters page.
- Fully virtualised on a Mac using Parallels Desktop Pro with bridged networking.
- One control-plane VM, one worker VM, one load balancer VM, and one Kubespray VM (bastion).
- One container network interface (CNI) and one ingress.
- No local registry, no GitOps with Flux, and no SOPS.
| VM role | vCPU | RAM | Disk | Network | Notes |
|---|---|---|---|---|---|
| Template (Ubuntu 24.04.3 LTS) | 2 | 2 GB | 64 GB (thin) | Bridged | Powered off before linking clones. |
| Ansible (Kubespray) | 2 | 2 GB | 64 GB (thin) | Bridged | Powered off after deployment. |
| Load balancer (HAProxy + Keepalived) | 1 | 2 GB | 64 GB (thin) | Bridged | Could reduce to 1 GB RAM. |
| Control plane (control plane + etcd) | 4 | 4 GB | 64 GB (thin) | Bridged | Core Kubernetes management node. |
| Worker | 2 | 4 GB | 64 GB (thin) | Bridged | Runs workloads and pods. |
Resource summary
| Resource | Allocation | Notes |
|---|---|---|
| Total VM RAM | ~10 GB | Combined memory across three powered VMs. |
| macOS available | ~6 GB | Helps stability and avoids swap usage. |
Expansion to hybrid cluster
VM capacity limits real workloads. A single virtual worker can only run so many pods before contention appears and new pods will not start.
- A physical worker adds realistic CPU scheduling, memory bandwidth, disk I/O, and NIC throughput.
- It lets me test real NICs, disks, and drivers while keeping the control plane simple and virtualised.
A practical development environment mirrors production without the overhead. This is where I prove ideas, write runbooks, and rehearse changes before they reach production.
See the build on the Hybrid Cluster page.
Goals
- Rebuild fast from scratch with a single, repeatable path.
- Stay close enough to production for networking, certificates, and ingress to behave the same.
- Keep it safe to experiment without risking data or uptime.
- Make it observable and reversible, with backups and a timed restore path.
Out of scope
- Local registry and chart mirrors.
- GitOps with Flux.
- SOPS for secrets.