
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
You need full control over your infrastructure, but managed services like EKS or GKE are too expensive or restricted by compliance requirements. To deploy Kubernetes with Kubespray effectively, you must treat it as an infrastructure-as-code project rather than a simple script execution. This approach ensures your cluster is reproducible, auditable, and aligned with security standards like ISO 27001 or SOC 2.
inventory-builder, configure group_vars for CNI and HA, and run the cluster.yml Ansible playbook. It provisions a production-ready, CNCF-conformant cluster on bare metal or VMs with support for air-gapped environments and multiple container runtimes.Why should you deploy Kubernetes with Kubespray instead of managed services?
Managed Kubernetes is excellent for reducing operational overhead, but it isn't always the right fit. In my work with Nepali fintech companies and government projects, data residency laws often prohibit storing data outside specific physical borders where no managed K8s exists. Similarly, organizations pursuing strict compliance certifications sometimes require direct access to the control plane configuration that managed providers abstract away.
Kubespray fills this gap by providing a battle-tested set of Ansible playbooks that produce upstream-compliant clusters. Unlike kubeadm alone, which handles node bootstrapping but leaves networking and high-availability architecture to you, Kubespray integrates these concerns into a single declarative workflow. If you are already managing servers with Ansible playbooks for server automation, adopting Kubespray feels like a natural extension rather than a new toolchain. It supports air-gapped deployments, multiple CNIs (Calico, Cilium, Flannel), and all major Linux distributions, making it the de facto standard for self-hosted enterprise Kubernetes.
How do you prepare infrastructure before running Kubespray?
The most common failure mode I see isn't the playbook itself—it's the underlying infrastructure. Kubespray assumes a clean, consistent base. Before you even clone the repo, verify these prerequisites across every target node.
Operating system and resource baselines
Kubespray supports Ubuntu 22.04/24.04, Debian 12, Rocky Linux 9, and AlmaLinux 9. Stick to LTS releases. Each control plane node needs at least 2 vCPUs and 4GB RAM; workers need 2 vCPUs and 8GB RAM minimum for production workloads. Disk I/O matters more than capacity: use SSD-backed storage for etcd directories (/var/lib/etcd) specifically. Spinning disks here will cause leader elections and cluster instability under load.
Network and firewall requirements
All nodes must have unique hostnames, MAC addresses, and product UUIDs. Disable swap permanently (swapoff -a and remove from /etc/fstab). Ensure these ports are open between nodes:
- TCP 6443: Kubernetes API server
- TCP 2379-2380: etcd client and peer communication
- TCP 10250: Kubelet API
- TCP 179: Calico BGP (if using Calico CNI)
- UDP 4789: VXLAN overlay (if using Flannel/VXLAN)
If you're deploying in a restricted environment, review firewall configuration best practices to avoid blocking critical inter-node traffic. Time synchronization via chrony or systemd-timesyncd is non-negotiable; certificate validation fails with clock skew greater than five minutes.
SSH and user access
Kubespray requires passwordless SSH from your Ansible controller to all targets. Use a dedicated user with sudo privileges rather than root. Configure ssh-agent forwarding if you're jumping through a bastion host. Test connectivity with ansible all -m ping -i inventory/mycluster/hosts.yaml before proceeding.
How do you configure inventory and group variables correctly?
Configuration determines whether your cluster survives its first node failure. Never edit files directly in the Kubespray source tree. Instead, generate a separate inventory directory to keep your configuration version-controlled and upgrade-safe.
# Generate inventory skeleton
cp -rfp inventory/sample inventory/mycluster
# Build hosts file dynamically from IP list
declare -a IPS=(10.10.1.10 10.10.1.11 10.10.1.12 10.10.1.20 10.10.1.21)
CONFIG_FILE=inventory/mycluster/hosts.yaml python3 contrib/inventory_builder/inventory.py ${IPS[@]} Defining high availability topology
Edit inventory/mycluster/hosts.yaml to assign roles explicitly. For production, always use three or five control plane nodes (odd numbers prevent split-brain). Etcd can be colocated on control plane nodes for smaller clusters or deployed on dedicated nodes for large-scale deployments.
all:
hosts:
node1:
ansible_host: 10.10.1.10
ip: 10.10.1.10
access_ip: 10.10.1.10
node2:
ansible_host: 10.10.1.11
ip: 10.10.1.11
access_ip: 10.10.1.11
node3:
ansible_host: 10.10.1.12
ip: 10.10.1.12
access_ip: 10.10.1.12
children:
kube_control_plane:
hosts:
node1:
node2:
node3:
kube_node:
hosts:
node1:
node2:
node3:
node4:
node5:
etcd:
hosts:
node1:
node2:
node3: Critical group_vars settings
Override defaults in inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml. These three settings matter most:
- Container runtime: Set
container_manager: containerd. Docker was removed as a runtime in Kubernetes 1.24+; containerd is now the stable default. - CNI plugin: Choose
kube_network_plugin: calicofor most on-prem deployments. Calico provides network policies out-of-the-box, essential for multi-tenant security. Use Cilium if you need eBPF-based observability. - API server load balancer: Set
loadbalancer_apiserver_localhost: trueandloadbalancer_apiserver_type: nginx. This deploys a local NGINX load balancer on each node pointing to all control planes, eliminating the need for an external LB in bare-metal setups.
What are the key differences between Kubespray, kubeadm, and managed Kubernetes?
Choosing the right deployment method depends on your team's expertise, budget, and compliance constraints. This comparison reflects real trade-offs I've navigated across dozens of production deployments.
| Criteria | Kubespray | kubeadm (Manual) | Managed (EKS/GKE/AKS) |
|---|---|---|---|
| Setup Complexity | Moderate (Ansible knowledge required) | High (manual component integration) | Low (API-driven provisioning) |
| Control Plane Access | Full (etcd, API server flags, certs) | Full | Limited (no etcd access, restricted flags) |
| Upgrade Path | Automated via playbook versions | Manual, error-prone | One-click, managed rollback |
| Air-Gap Support | Native (offline registry + artifacts) | Possible but manual | Not available |
| Operational Overhead | High (you own everything) | Very High | Low (provider manages control plane) |
| Cost Model | Infrastructure only (no management fee) | Infrastructure only | Management fee + infrastructure |
| Compliance Flexibility | Maximum (full audit trail, custom hardening) | Maximum | Constrained by provider controls |
Choose Kubespray when you need managed-service-like reproducibility without surrendering control. Choose kubeadm only for learning or highly customized single-cluster scenarios. Choose managed services when operational headcount is limited and compliance allows it.
How do you execute the deployment and validate cluster health?
With inventory and variables configured, run the cluster playbook. Always use the --become flag and specify your inventory path explicitly.
ansible-playbook -i inventory/mycluster/hosts.yaml \
--become --become-user=root \
cluster.yml This takes 15–30 minutes depending on node count and network speed. Kubespray is idempotent: safe to re-run if interrupted. After completion, copy the kubeconfig:
# Copy admin kubeconfig to local machine
scp root@node1:~/.kube/config ~/.kube/mycluster-config
export KUBECONFIG=~/.kube/mycluster-config
# Validate all nodes are Ready
kubectl get nodes -o wide
# Verify core components
kubectl get pods -n kube-system
kubectl cluster-info dump | grep -E 'etcd|apiserver|controller' Post-deployment hardening checklist
A fresh Kubespray cluster isn't production-ready until you address these items:
- Enable Pod Security Standards: Enforce
restrictedpolicy on namespaces to prevent privileged containers. - Configure etcd backups: Schedule automated snapshots to object storage. Test restoration quarterly.
- Install monitoring: Deploy Prometheus stack via Helm. Alert on etcd latency, API server errors, and node pressure.
- Rotate certificates: Kubespray generates certs valid for one year. Document rotation procedures before expiry.
- Restrict RBAC: Remove cluster-admin bindings from default users. Implement least-privilege service accounts.
For teams integrating AI-assisted operations, consider how AIOps practices can enhance monitoring and anomaly detection on self-hosted clusters without sending telemetry to third parties.
Deploy Kubernetes with Kubespray as a long-term platform strategy
When you deploy Kubernetes with Kubespray, you're choosing operational sovereignty over convenience. This trade-off pays dividends in regulated industries, cost-sensitive environments, and regions where cloud providers lack local presence. The initial investment in Ansible proficiency and infrastructure discipline yields clusters that survive audits, outlast vendor lock-in, and scale on your terms.
Start with a three-node test cluster to build muscle memory before touching production. Version-control your inventory and group_vars alongside application code. Treat upgrades as planned events, not emergencies. And remember: automation without observability is just faster failure. Pair every deployment with comprehensive monitoring from day one.
If you're evaluating self-hosted Kubernetes for a compliance-sensitive project or need guidance on production hardening, reach out to discuss your infrastructure requirements. I help teams architect clusters that pass audits and handle real traffic—not just demo environments.