Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Deploy Kubernetes with Kubespray

By Kokil Thapa | Last reviewed: August 2026

If you need full control over your infrastructure without cloud vendor lock-in, you likely want to deploy Kubernetes with Kubespray. This Ansible-based toolkit provisions production-grade clusters on bare metal or virtual machines, handling the complex plumbing of etcd, container runtimes, and networking that manual installs get wrong. For teams building CI/CD pipeline automation or self-hosted platforms in Nepal or abroad, Kubespray offers a repeatable, auditable alternative to managed services.

Why deploy Kubernetes with Kubespray instead of kubeadm?

Kubeadm is excellent for learning and single-node setups, but it stops at the bootstrap phase. You still need to manually configure CNI plugins, set up high-availability etcd, manage certificate rotation, and harden security policies. Kubespray wraps all of this into a single, idempotent Ansible workflow that treats infrastructure as code.

In my experience working on production infrastructure for legal-tech portals and eCommerce systems, the gap between "kubeadm init succeeded" and "production-ready cluster" is where most projects stall. Kubespray bridges that gap by baking in best practices for HA etcd, node labeling, taints, and network policies from day one.

kubeadm (Manual)Bootstrap onlyManual CNI installManual HA etcd configCustom hardening scriptsSeparate upgrade playbooksKubespray (Automated)Full cluster lifecycleCNI auto-configuredHA etcd built-inSecurity defaults appliedIdempotent upgrades
kubeadm requires manual post-bootstrap steps; Kubespray automates the entire deploy Kubernetes with Kubespray lifecycle

When Kubespray is the right choice

  • Bare metal or VM environments where cloud controllers are unavailable
  • Air-gapped or restricted-network deployments requiring offline artifact mirroring
  • Multi-cloud hybrid setups needing consistent configuration across providers
  • Teams already standardized on Ansible for configuration management
  • Regulated environments requiring auditable, version-controlled infrastructure

When to reconsider

If you're running a single development node, kubeadm or kind is faster. If you're fully committed to AWS/GCP/Azure and don't need portability, managed EKS/GKE/AKS removes operational burden. Kubespray shines when you own the infrastructure and need reproducibility.

How do you prepare nodes before you deploy Kubernetes with Kubespray?

Kubespray expects clean, minimally configured Linux hosts. Skipping preparation causes cryptic Ansible failures mid-playbook. On Ubuntu 22.04/24.04 LTS (the most common target in 2026), follow these steps on every node.

Base system requirements

<!-- Run on ALL target nodes -->
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3 python3-pip curl apt-transport-https ca-certificates gnupg lsb-release

# Disable swap (Kubernetes requirement)
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab

# Load required kernel modules
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay br_netfilter

# Set sysctl parameters
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system

SSH and user configuration

Kubespray connects via SSH. Ensure passwordless sudo works for your Ansible user. Create a dedicated k8s-deploy user rather than reusing root:

# On each node
sudo useradd -m -s /bin/bash k8s-deploy
echo 'k8s-deploy ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/k8s-deploy
sudo mkdir -p /home/k8s-deploy/.ssh
sudo cp ~/.ssh/authorized_keys /home/k8s-deploy/.ssh/
sudo chown -R k8s-deploy:k8s-deploy /home/k8s-deploy/.ssh

Firewall rules

Open only what Kubernetes needs. UFW example for control plane nodes:

sudo ufw allow 22/tcp    # SSH
sudo ufw allow 6443/tcp  # kube-apiserver
sudo ufw allow 2379:2380/tcp  # etcd
sudo ufw allow 10250/tcp # kubelet
sudo ufw allow 10259/tcp # kube-scheduler
sudo ufw allow 10257/tcp # kube-controller-manager
sudo ufw reload

Worker nodes need ports 22, 10250, and the NodePort range (default 30000-32767) if you expose services that way. Document your firewall policy alongside your server security hardening documentation.

How do you configure inventory and variables to deploy Kubernetes with Kubespray?

The inventory defines your cluster topology. Variables control everything from CNI selection to container runtime. Getting these right prevents re-running the playbook multiple times.

Generate the inventory

# Clone specific release tag (never use master in production)
git clone --branch v2.26.0 https://github.com/kubernetes-sigs/kubespray.git
cd kubespray

# Generate inventory from IP list
declare -a IPS=(10.0.1.10 10.0.1.11 10.0.1.12 10.0.1.20 10.0.1.21)
CONFIG_FILE=inventory/mycluster/hosts.yaml python3 contrib/inventory_builder/inventory_builder.py ${IPS[@]}

This creates inventory/mycluster/hosts.yaml with sensible defaults: first three IPs become control plane + etcd nodes, remaining become workers. Edit it to match your actual topology.

Kubespray Inventory TopologyControl Planekube-apiserverkube-schedulerkube-controlleretcd ClusterDistributed KV storeHA: 3+ nodesPeer communicationWorker Nodeskubelet + kube-proxyPod executionCNI endpointsShared Configuration (group_vars/all.yml)container_manager: containerd | kube_network_plugin: calicokube_version: v1.31.0 | etcd_deployment_type: kubeadm
Node roles and shared configuration when you deploy Kubernetes with Kubespray on bare metal

Critical group_vars settings

Edit inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml. These are the variables I adjust on nearly every project:

# Container runtime (containerd is default and recommended in 2026)
container_manager: containerd

# Kubernetes version (pin explicitly, never rely on defaults)
kube_version: v1.31.0

# CNI plugin selection
kube_network_plugin: calico
# Alternatives: cilium, flannel, weave
# Calico is safest default for bare metal; Cilium for eBPF features

# Service CIDR and Pod CIDR (must not overlap host networks)
kube_service_addresses: 10.233.0.0/18
kube_pods_subnet: 10.233.64.0/18

# Enable audit logging for compliance
kubernetes_audit: true

# Disable dashboard in production (security risk)
dashboard_enabled: false

Etcd configuration

For production, always use external etcd or kubeadm-managed etcd with at least three nodes. Edit group_vars/etcd.yml:

etcd_deployment_type: kubeadm
etcd_metrics: basic
# Enable TLS between etcd peers (default true, never disable)
etcd_peer_client_auth: true

What networking and CNI options work best when you deploy Kubernetes with Kubespray?

CNI choice determines your cluster's networking capabilities, performance characteristics, and operational complexity. There is no universal best option — only the right trade-off for your workload.

CNI PluginBest ForComplexityKey Trade-offs
CalicoGeneral-purpose bare metal, network policiesLowMature, well-documented, BGP routing optional
CiliumeBPF observability, advanced security policiesMediumHigher resource usage, kernel ≥5.8 required
FlannelSimple overlays, learning environmentsVery LowNo native network policy, limited scalability
WeaveEncrypted multi-cloud, legacy compatibilityLowSlower performance, smaller community in 2026

For most Nepal-based deployments I've worked on — legal-tech portals, eCommerce backends, booking systems — Calico is the pragmatic default. It handles network policies, works reliably on Ubuntu 22.04/24.04, and doesn't require exotic kernel features. Choose Cilium only if you specifically need eBPF-based observability or L7-aware policies and have verified kernel compatibility.

Load balancer strategy for HA control plane

Kubespray supports multiple approaches for distributing API server traffic across control plane nodes:

  • kube-vip (recommended): Lightweight, runs as static pod, provides VIP + ARP/NDP announcement
  • HAProxy + Keepalived: Traditional, well-understood, separate from K8s lifecycle
  • External LB: Hardware F5, cloud LB, or MetalLB for bare metal service exposure

Set loadbalancer_apiserver.type: kube-vip in k8s-cluster.yml for the simplest HA setup. Define apiserver_loadbalancer_domain_name and loadbalancer_apiserver.address with your chosen VIP.

How do you execute the playbook and validate after you deploy Kubernetes with Kubespray?

With inventory and variables configured, run the cluster playbook. Use a virtual environment and pin dependencies to avoid breakage.

Install dependencies and run

# Create isolated Python environment
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# Dry-run first to catch configuration errors
ansible-playbook -i inventory/mycluster/hosts.yaml cluster.yml --check --diff

# Actual deployment (expect 15-45 minutes depending on node count/network)
ansible-playbook -i inventory/mycluster/hosts.yaml cluster.yml -b -v

The -b flag enables privilege escalation. The -v flag provides visibility into task execution. Never skip the dry-run on a fresh inventory — it catches misconfigured IPs, unreachable hosts, and variable typos before they waste 30 minutes of playbook time.

InventoryParse hosts.yamlPre-checksValidate OS/SSHetcd BootstrapHA cluster initControl PlaneAPI + schedulerWorkersJoin + CNIPost-Deploy Validation Checklistkubectl get nodes → All Ready | kubectl get pods -A → No CrashLoopBackOffetcdctl endpoint health → All healthy | CoreDNS resolving | CNI pods RunningCommon Failure PointsSwap not disabled | Firewall blocking etcd ports | Time sync drift >500msMismatched kube_version | DNS resolution failure on nodes
Execution pipeline and validation steps after you deploy Kubernetes with Kubespray

Post-deployment validation

Copy the admin kubeconfig and verify cluster health immediately:

# Copy kubeconfig (adjust path based on inventory)
scp root@10.0.1.10:/root/.kube/config ~/.kube/kubespray-cluster
export KUBECONFIG=~/.kube/kubespray-cluster

# Verify all nodes are Ready
kubectl get nodes -o wide

# Check system pods (all should be Running)
kubectl get pods -n kube-system

# Validate etcd health
ETCDCTL_API=3 etcdctl --endpoints=https://10.0.1.10:2379 \
  --cacert=/etc/ssl/etcd/ssl/ca.pem \
  --cert=/etc/ssl/etcd/ssl/admin-node1.pem \
  --key=/etc/ssl/etcd/ssl/admin-node1-key.pem \
  endpoint health --cluster

# Test DNS resolution
kubectl run dns-test --rm -it --image=busybox:1.36 --restart=Never -- nslookup kubernetes.default

Troubleshooting common failures

If nodes stay NotReady, check kubelet logs: journalctl -u kubelet -f. The most frequent causes are swap still enabled, missing kernel modules, or CNI pods failing to start. If etcd health checks fail, verify time synchronization across all nodes — even 500ms drift breaks raft consensus. Use chronyc sources or timedatectl status to confirm NTP is active.

For persistent issues, re-run with increased verbosity: ansible-playbook cluster.yml -vvv. Kubespray is idempotent; safe re-runs won't corrupt a partially deployed cluster. If you need to reset entirely, use reset.yml before re-running cluster.yml.

Conclusion

Learning to deploy Kubernetes with Kubespray gives you portable, auditable cluster provisioning that survives cloud vendor changes and budget constraints. Pin your versions, validate node preparation thoroughly, choose Calico unless you have specific eBPF requirements, and always run post-deploy health checks before scheduling workloads. The upfront investment in proper configuration pays off during upgrades, incident response, and compliance audits. If you're evaluating whether self-managed Kubernetes makes sense for your Nepal-based or global infrastructure, reach out to discuss your specific architecture before committing to a platform.

Frequently Asked Questions

Kubespray is an Ansible-based production-grade Kubernetes installer. Unlike basic kubeadm, it handles HA setup, CNI plugins, and OS hardening automatically for bare metal or VMs.

Software is free. Infrastructure costs vary; three minimal VMs run roughly NPR 15,000/month (USD 112) on cloud providers, excluding engineering time for setup and ongoing maintenance.

Control planes need 2 vCPUs and 4GB RAM minimum. Workers require 2 vCPUs and 8GB RAM for production workloads. I recommend 4 vCPUs per node to avoid resource contention during upgrades.

Kubespray supports Ubuntu 22.04/24.04 LTS, Debian 12, Rocky Linux 9, AlmaLinux 9, and Fedora CoreOS. In my experience, Ubuntu 22.04 LTS remains the most stable choice for Nepal-based deployments due to wider community testing and fewer SELinux complications compared to RHEL derivatives. Always verify the specific release notes for your target Kubespray version before provisioning nodes, as support matrices change between minor releases.

Enable kube_control_plane on at least three nodes in your inventory. Set etcd_deployment_type to kubeadm or host depending on preference. Kubespray automatically configures HAProxy or Nginx load balancers for API server access when multiple control plane nodes exist. On client projects, I always deploy three control planes minimum; two-node HA splits brain during network partitions and causes more outages than it prevents.

Yes, using scale.yml playbook with --limit targeting new nodes only. Update inventory first, then run ansible-playbook -i inventory/mycluster/hosts.yaml scale.yml -b -v --limit=new_worker_hostname. Never re-run cluster.yml on existing nodes unless performing full upgrades. I have recovered broken clusters where operators accidentally re-ran full provisioning instead of scale playbooks. Always backup etcd snapshots before scaling operations in production environments.

containerd is now the default and recommended runtime since Docker shim removal. Configure container_manager to containerd in k8s-cluster.yml. Avoid CRI-O unless you have specific compliance requirements. On production Laravel applications running on Kubernetes, containerd provides better memory efficiency and faster pod startup times. Migration from Docker to containerd requires reinstalling nodes; plan this during maintenance windows rather than attempting in-place conversions that often leave orphaned containers.

Run renew-certs.yml playbook annually or before certificate expiry. Kubespray regenerates all PKI assets and restarts affected components automatically. Monitor certificate expiration via Prometheus alerts or manual checks. I have seen production outages caused by expired certificates because teams forgot renewal schedules. Set calendar reminders six weeks before expiry. Test renewal procedures in staging first; certificate rotation can fail silently if etcd endpoints are misconfigured in inventory files.

Calico offers the best balance of performance, network policies, and BGP routing for bare metal deployments. Cilium provides advanced observability but requires newer kernels. Flannel suits simple flat networks without policy needs. For legal-tech portals handling sensitive documents, I consistently choose Calico for its mature network policy enforcement and predictable performance. Avoid Weave in 2026; development has slowed significantly and compatibility issues with newer Kubernetes versions appear frequently in production troubleshooting scenarios.

Check Ansible output for specific task failures first. SSH into failed nodes and examine journalctl -u kubelet and container logs. Verify firewall rules allow required ports (6443, 2379-2380, 10250-10252). Common failures include insufficient RAM, disabled swap, or missing kernel modules. On real deployments, DNS resolution failures between nodes cause cryptic errors; test connectivity with ping and nslookup before blaming Kubespray. Always run with -vvv verbosity to see exact command outputs during debugging sessions.

For most SMBs, managed Kubernetes or single-server Docker Compose is more practical. Kubespray makes sense only when you need multi-node HA, regulatory data residency, or custom networking. Budget NPR 200,000+ (USD 1,500) minimum for initial setup plus ongoing DevOps time. I have built legal-tech platforms on Kubespray where data sovereignty requirements mandated local hosting, but for typical eCommerce or service sites, the operational overhead rarely justifies self-managed clusters versus simpler deployment strategies.

Payment integration happens at application level, not cluster level. Deploy your Laravel or Node.js app with proper environment variables for gateway credentials stored in Kubernetes Secrets. Use ExternalName services or ingress annotations for webhook callbacks from payment providers. Ensure TLS termination occurs correctly; Nepali payment gateways reject non-HTTPS callbacks. On eCommerce projects, I test payment flows extensively in staging with sandbox credentials before production cutover, as debugging live transactions in distributed systems adds significant complexity.

Disable root SSH access, enable UFW with explicit allow rules, configure fail2ban for SSH brute force protection, and apply CIS Kubernetes Benchmark recommendations via kube-bench. Restrict etcd access to control plane nodes only. Enable audit logging for compliance-sensitive workloads. On legal-tech portals, I additionally implement network policies isolating database pods from public-facing services. Regular vulnerability scanning with Trivy and timely patching matter more than exotic security tools; most breaches exploit unpatched systems, not architectural weaknesses.

Follow official upgrade documentation strictly: upgrade one minor version at a time, never skip versions. Run upgrade-cluster.yml with --limit targeting control planes first, then workers sequentially. Backup etcd before starting. Expect 30-60 minutes downtime per node depending on workload draining. On production systems, I schedule upgrades during low-traffic windows and maintain rollback plans. Test upgrades in identical staging environments first; configuration drift between dev and prod causes most upgrade failures I encounter in practice.

Choose managed Kubernetes (EKS, GKE, DigitalOcean) if you lack dedicated DevOps staff or need rapid scaling. Use kubeadm for learning or simple single-control-plane setups. Consider Rancher or OpenShift for enterprise multi-cluster management. Kubespray excels for bare metal, air-gapped, or highly customized deployments where you control every layer. For most Nepal businesses without full-time platform engineers, managed services reduce operational burden significantly despite higher monthly costs versus self-hosted alternatives.

Share this article

Quick Contact Options
Choose how you want to connect me: