Kubernetes, often called k8s, runs and coordinates containerized applications across one or more computers. This guide shows how to build a small Linux cluster with kubeadm, using one control-plane node and one worker node; you will need administrator access and a compatible container runtime on each machine.
This is a practical self-managed setup for learning or a controlled environment, not a recommendation to expose a new cluster directly to the public internet. For production, plan availability, access control, backups, upgrades, monitoring, and network security before deploying workloads. Kubernetes changes over time, so check the official documentation for version-specific requirements and supported installation commands.
What You’ll Need
- Two Linux machines or virtual machines: one for the control plane and at least one worker. Use a currently supported Linux distribution and confirm the selected Kubernetes release supports it.
- Administrator privileges on every machine, plus a shell and a way to connect to each machine.
- A stable network between the nodes. Each machine needs a unique hostname, MAC address, and product UUID, and the nodes must be able to reach one another on the ports required by your chosen networking and cluster configuration.
- A compatible container runtime, such as containerd, installed and configured on all nodes. Configure its cgroup driver to match the system configuration recommended for your Kubernetes release.
- Enough memory and CPU for the operating system, Kubernetes services, and your test workloads. Check the current kubeadm system requirements rather than relying on an assumed minimum.
- A network plugin compatible with your Kubernetes version. The plugin provides pod-to-pod networking; kubeadm does not install one automatically.
- Time to choose a Kubernetes version and keep the control-plane and node package versions within the supported version-skew policy.
Step-by-Step
- Choose the cluster layout and release. Decide which machine will be the control plane and which will be a worker. For this walkthrough, use a single control-plane node and a separate worker. Select a Kubernetes release supported by your operating system, runtime, and network plugin. Record the intended version and use the same release line for kubeadm, kubelet, and kubectl unless the version-skew policy specifically permits a difference.
- Prepare each Linux machine. Set a unique hostname on every node and ensure name resolution works consistently, either through DNS or suitable local host entries. Confirm that each node has a unique network identity and a stable IP address that other nodes can reach. Apply operating-system updates, then reboot if required. Avoid changing a node’s address after the cluster has been initialized unless you understand the consequences for cluster configuration.
- Configure memory and networking prerequisites. Kubernetes expects swap to be disabled unless you have deliberately configured a supported swap setup for your release. Follow the distribution’s instructions to disable it persistently, then verify the result after reboot. Enable the required kernel modules and network settings described in the kubeadm and network-plugin documentation, including packet forwarding where required. Do not copy commands for a different Linux distribution without checking what they change.
- Install and configure the container runtime. Install a runtime supported by Kubernetes, then make sure its service starts automatically and is running. Configure the runtime’s cgroup driver as recommended for your environment; mismatched cgroup settings can prevent kubelet from registering or staying healthy. Check the runtime status and logs before installing Kubernetes components. Use the runtime’s official installation and configuration instructions for your operating system.
- Install kubeadm, kubelet, and kubectl on every node. Configure the package source using the official Kubernetes instructions for your distribution and selected release. Install the three packages, and prevent unattended package updates from unexpectedly changing their versions. The kubelet is the node agent, kubeadm performs cluster setup, and kubectl is the command-line client. It is fine to install kubectl only where you will administer the cluster, but kubeadm and kubelet are needed on cluster nodes.
- Initialize the control plane. On the control-plane machine, run kubeadm init using the options appropriate for your network and release. If your pod network plugin requires a specific pod CIDR, include the value specified by that plugin’s documentation; do not choose a conflicting range already used on your network. Save the success output securely. It includes instructions for configuring kubectl and a join command with a temporary authentication token. Treat that command as sensitive, and do not post it in public logs or chat.
- Configure kubectl access. Follow kubeadm’s printed instructions to copy the administrator configuration into your user’s Kubernetes configuration location, with appropriate ownership and permissions. Alternatively, use the documented configuration path for a dedicated administrative account. Run kubectl get nodes and kubectl get pods -n kube-system to inspect the new control plane. A node may show NotReady until pod networking is installed; that is expected at this stage.
- Install the pod network plugin. Apply the plugin’s official installation instructions and version-compatible configuration. Use its documented manifest or package method, and check whether it requires a pod CIDR, special kernel settings, or particular firewall rules. Wait for the plugin’s pods to become ready, then inspect system pods and node status again. Do not install multiple network plugins on the same cluster; overlapping network components can break connectivity.
- Join the worker node. On the worker, run the kubeadm join command produced by kubeadm init. Use the control-plane address that the worker can reach, and keep the token and discovery hash intact. If the token has expired or was lost, create a fresh join command on the control plane using kubeadm’s documented procedure. From the control plane, run kubectl get nodes and wait for the worker to register and become Ready.
- Verify the cluster and test a workload. Check node status and confirm that the system pods are healthy. Deploy a small, noncritical test workload using a manifest you understand, then inspect its pod status and logs. If you create a Service, verify its access behavior from the appropriate network; a pod being Running does not by itself prove that external access is configured. Remove the test resources when finished.
Common Mistakes
Skipping the network plugin: kubeadm initializes the control plane but does not provide pod networking. Without a compatible plugin, nodes and system pods may remain NotReady or fail to communicate.
Using a conflicting pod CIDR: A pod network range that overlaps with a machine, home, campus, or corporate network can cause hard-to-diagnose routing failures. Check existing routes and the plugin’s requirements before initialization.
Leaving swap enabled or mismatching cgroup drivers: Either issue can make kubelet fail or behave inconsistently. Confirm the actual runtime and operating-system settings rather than assuming defaults match.
Mixing incompatible versions: Do not independently install the newest available version of each package. Follow Kubernetes version-skew guidance and use a compatible network plugin release.
Blocking required traffic: Host firewalls, cloud security rules, or campus network controls may prevent node communication. Allow only the ports required by your cluster components and chosen plugin; do not solve a connectivity problem by disabling the firewall permanently.
Misplacing cluster credentials: The kubeconfig and join token can grant meaningful access. Restrict file permissions, avoid sharing credentials, and rotate or replace temporary join credentials according to the documentation.
Tips
- Keep a written record of node names, addresses, Kubernetes versions, runtime settings, pod CIDR, and network-plugin version. This makes later troubleshooting and upgrades easier.
- Use a disposable virtual-machine environment for practice. A local learning distribution can be simpler for experimentation, but its setup differs from a kubeadm cluster and may hide networking or multi-node issues.
- Start with a harmless test workload and learn how to inspect pods, events, logs, and node conditions before deploying important applications.
- Plan backups for cluster state and application data separately. A control-plane backup does not automatically preserve data stored by applications on separate volumes.
- Before upgrading, read the release notes and supported upgrade path, back up important state, and upgrade components in the documented order. Avoid upgrading all nodes at once without a recovery plan.
- For a real service, add authentication, role-based access control, resource requests and limits, monitoring, and a tested recovery process before treating the cluster as production-ready.
Troubleshooting
kubeadm init fails before creating the control plane: Read the first reported error and check prerequisites on that machine: swap state, runtime health, cgroup configuration, required kernel settings, free resources, and address selection. Correct the underlying issue before retrying. If a previous attempt partially initialized the node, follow kubeadm’s documented reset procedure only after confirming that the node contains no cluster data you need.
A node stays NotReady: Inspect its conditions with kubectl describe node and check system pods in the kube-system namespace. If networking pods are not ready, review the plugin’s logs and confirm its configuration matches the pod CIDR and operating system. If kubelet is unhealthy, inspect the service status and logs on that node, then verify runtime connectivity and cgroup settings.
The worker cannot join: Confirm that the control-plane address resolves and is reachable from the worker, that required firewall rules permit the connection, and that the join token and discovery hash are valid. Check that the worker’s clock and package versions are reasonable for the cluster’s supported version skew. Generate a fresh join command if the token is no longer valid.
Pods remain Pending or cannot reach one another: Use kubectl describe pod to read scheduling events, then check whether nodes have enough available resources and whether the network plugin is healthy. Inspect routes and firewall rules for overlaps or blocked traffic. A Pending pod may be a resource or scheduling issue rather than a networking failure.
kubectl cannot connect: Verify that you are using the intended kubeconfig file and that its permissions and server address are correct. From the control plane, check kubelet and control-plane component health. Do not copy an administrator configuration to an untrusted machine as a quick workaround.
When an error remains unclear, preserve the exact error text, relevant service logs, Kubernetes version, operating system, and recent changes. Compare those details with the official documentation for the same release before applying fixes from unrelated versions.
Frequently Asked Questions
What does “k8s” mean?
K8s is a common shortened form of Kubernetes: the “8” represents the eight letters between the first and last letters of the name. It refers to the same container orchestration platform.
Can I install Kubernetes on one machine?
Yes. kubeadm can create a single-node learning cluster, though the usual multi-node pattern separates the control plane and workloads. For a one-machine lab, follow the single-node instructions for your chosen setup and understand that it does not provide machine-level availability.
Does kubeadm install the container runtime and network plugin?
No. You install and configure a compatible runtime before initialization, and you install a pod network plugin afterward. kubeadm manages cluster setup but does not supply those components for you.
Can I run workloads on the control-plane node?
By default, Kubernetes marks a control-plane node so ordinary workloads are not scheduled there. For a disposable single-node lab, you can remove the scheduling restriction using the documented kubectl command. Avoid doing this on a cluster where control-plane capacity should be reserved.
Is this setup suitable for production?
Not by itself. A basic kubeadm cluster is a starting point, not a complete production design. Production needs depend on workload and organization, but commonly include resilient control-plane planning, secure access, backups, monitoring, storage, upgrade procedures, and tested incident recovery.
What should I do if I want to remove the cluster?
First preserve any application data you need. Then follow kubeadm’s documented reset procedure on each node and remove leftover network-plugin or runtime configuration only as instructed. Resetting a node can delete cluster-related state, so verify the target and backups before proceeding.