Skip to main content

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).
note

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

#ToolVersionType or licenceNotes
1MacBook Air M3 (15-inch, 16 GB RAM, 500 GB SSD)macOS 15.6.1Commercial hardware and OS (Apple proprietary).Primary host system.
2Parallels Desktop Pro26.0.1Commercial (subscription).Used for running Ubuntu VMs.
3High Speed Internet1Gbps Fibre to the Premises.Provides Internet connectivity.
4Router/Firewall/SwitchConsumer hardware or proprietary firmware.Provides LAN and Internet connectivity.
5Ubuntu Server LTS24.04.3 LTSFree and open-source (Canonical Ltd., GPL).Base operating system for all nodes.
6Kubernetes1.32.9Free and open-source (CNCF, Apache 2.0).Cluster orchestration platform.
7Kubespray2.29.0Free 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 rolevCPURAMDiskNetworkNotes
Template (Ubuntu 24.04.3 LTS)22 GB64 GB (thin)BridgedPowered off before linking clones.
Ansible (Kubespray)22 GB64 GB (thin)BridgedPowered off after deployment.
Load balancer (HAProxy + Keepalived)12 GB64 GB (thin)BridgedCould reduce to 1 GB RAM.
Control plane (control plane + etcd)44 GB64 GB (thin)BridgedCore Kubernetes management node.
Worker24 GB64 GB (thin)BridgedRuns workloads and pods.

Resource summary

ResourceAllocationNotes
Total VM RAM~10 GBCombined memory across three powered VMs.
macOS available~6 GBHelps stability and avoids swap usage.

Expansion to hybrid cluster

note

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.