Skip to main content

Command Palette

Search for a command to run...

Part 1: Preparing Linux Nodes for Kubernetes

Updated
10 min read
Part 1: Preparing Linux Nodes for Kubernetes

Why node preparation matters

Most Kubernetes failures don’t start in Kubernetes itself; they start at the operating system level.

Unstable storage, incorrect time synchronization, broken DNS, or leftover defaults like swap can silently cause problems that are extremely difficult to debug later. Kubernetes assumes the underlying system is predictable and correctly configured.

In this part of the series, we’ll prepare Linux nodes so they are ready to run Kubernetes reliably. Nothing in this section is optional. If these steps are done correctly, the rest of the cluster setup becomes much smoother.


What we will do in this part

In this article, we will:

  • Create dedicated storage for Kubernetes and workloads

  • Disable swap permanently

  • Configure time synchronization (NTP)

  • Prepare networking and DNS for Kubernetes

  • Verify the system is ready for Kubernetes installation


Prerequisites

Before continuing, make sure you’ve read Part 0 – Before You Begin and that:

  • You have Linux nodes ready (RHEL / Rocky / Alma / CentOS Stream–like OR Ubuntu/Debian based flavors)

  • You have root or sudo access

  • Storage structure is already configured (Optional if you understand the storage of your machines)

  • Nodes have static IPs and proper hostnames

If any of these are missing, fix them before proceeding.


Creating dedicated storage for Kubernetes

Why separate storage is important

Kubernetes nodes generate data in two very different ways:

  1. Container runtime data (images, layers, logs)

  2. Persistent workload data (volumes used by applications)

Mixing this data with the root filesystem is risky. Disk pressure on / can break the node, and recovering from it is painful. Separating storage makes the system easier to operate and troubleshoot.

In this guide, we’ll create:

  • one mount for container runtime data

  • one mount for persistent workloads (used later by storage systems)


1. Create required mount directories

Purpose

Create directories that will later be used as mount points for the logical volumes.
The -p flag ensures parent directories are created if they don’t already exist.

Syntax

mkdir -p <directory_1> <directory_2>

Example

mkdir -p /home/longhorn_data /home/containerd_data

2. Create a logical volume for Containerd data

Purpose

Create a logical volume (LV) of a specified size inside an existing volume group (VG).
This LV will be dedicated to Containerd storage.

Syntax

lvcreate -L <size> -n <logical_volume_name> <volume_group_name>

Example

lvcreate -L 50G -n lv_containerd_data myvg
  • 50G → size of the logical volume

  • lv_containerd_data → logical volume name

  • myvg → existing volume group


3. Create a logical volume for Longhorn data

Purpose

Create a larger logical volume for Longhorn, which typically requires more storage for persistent volumes.

Syntax

lvcreate -L <size> -n <logical_volume_name> <volume_group_name>

Example

lvcreate -L 300G -n lv_longhorn_data myvg

4. Format the Containerd logical volume with XFS

Purpose

Format the logical volume with the XFS filesystem so it can be mounted and used.

Syntax

mkfs.xfs /dev/<volume_group>/<logical_volume>

Example

mkfs.xfs /dev/myvg/lv_containerd_data

5. Format the Longhorn logical volume with XFS

Purpose

Apply the XFS filesystem to the Longhorn logical volume.

Syntax

mkfs.xfs /dev/<volume_group>/<logical_volume>

Example

mkfs.xfs /dev/myvg/lv_longhorn_data

6. Mount the Containerd logical volume

Purpose

Attach the formatted logical volume to its directory so the OS can use it for storage.

Syntax

mount /dev/<volume_group>/<logical_volume> <mount_point>

Example

mount /dev/myvg/lv_containerd_data /home/containerd_data/

7. Mount the Longhorn logical volume

Purpose

Mount the Longhorn logical volume to its designated directory.

Syntax

mount /dev/<volume_group>/<logical_volume> <mount_point>

Example

mount /dev/myvg/lv_longhorn_data /home/longhorn_data/

8. Verify mounted filesystems

Purpose

Display disk usage and confirm that the new logical volumes are mounted correctly.

Syntax

df -h

This command shows:

  • Mounted filesystems

  • Total size

  • Used space

  • Available space

  • Mount points


9. Make the mounts persistent using /etc/fstab

Purpose

By default, mounts created using the mount command are not persistent and will be lost after a system reboot.
Adding entries to /etc/fstab ensures that the logical volumes are automatically mounted at boot time.

9.1 Get the UUID of the logical volumes

Purpose

Using UUIDs instead of device paths (/dev/myvg/...) is recommended because UUIDs remain consistent even if device names change.

Syntax

blkid /dev/<volume_group>/<logical_volume>

Example

blkid /dev/myvg/lv_containerd_data
blkid /dev/myvg/lv_longhorn_data

Sample output

/dev/myvg/lv_containerd_data: UUID="a1b2c3d4-e5f6-7890" TYPE="xfs"
/dev/myvg/lv_longhorn_data: UUID="f9e8d7c6-b5a4-3210" TYPE="xfs"

9.2 Add entries to /etc/fstab

Purpose

Define how and where each filesystem should be mounted during boot.

Syntax:

UUID=<uuid>  <mount_point>  <filesystem_type>  <options>  <dump>  <fsck>

Example:

UUID=a1b2c3d4-e5f6-7890  /home/containerd_data  xfs  defaults  0  0
UUID=f9e8d7c6-b5a4-3210  /home/longhorn_data    xfs  defaults  0  0

Field breakdown

Field

Description

UUID

Unique identifier of the filesystem

Mount point

Directory where the filesystem will be mounted

Filesystem type

xfs in this case

Options

defaults covers common mount options

Dump

Usually 0 (backup utility)

Fsck

0 for XFS (fsck not used at boot)


9.3 Validate /etc/fstab entries

Purpose

Verify that all /etc/fstab entries are correct without rebooting.
This step helps prevent boot failures caused by misconfigured fstab entries.

Syntax

mount -a

Example

mount -a

If the command returns no output, the configuration is valid.


9.4 Confirm persistent mounts

Purpose

Ensure that the logical volumes are mounted as expected.

Syntax

df -h | grep -E "containerd_data|longhorn_data"

10. Disabling swap (mandatory)

Why swap must be disabled

Kubernetes requires swap to be disabled so it can make reliable scheduling and memory management decisions. Leaving swap enabled can cause kubelet failures or unpredictable pod behavior.


10.1 Disable swap immediately

Purpose

Turn off all active swap devices and swap files on the system.

Syntax

swapoff -a

Expected output

(no output)

No output indicates swap was disabled successfully.


10.2 Verify current swap status

Purpose

Confirm whether any swap devices or files are still active.

Syntax

swapon --show

Expected output (swap disabled)

(no output)

Example output (swap enabled – before disabling)

NAME                    TYPE      SIZE   USED   PRIO
/dev/mapper/myvg-swap partition  16G    0B     -2

10.3 Remove swap entries from /etc/fstab

Purpose

Prevent swap from being automatically enabled during system boot.

Syntax (swap partition)

<swap_device>  none  swap  sw  0  0

Example

/dev/mapper/myvg-swap  none  swap  sw  0  0

Modified /etc/fstab entry

# /dev/mapper/myvg-swap  none  swap  sw  0  0

Commenting out the line disables swap persistence while keeping the reference for future use.


10.4 (Optional) Disable and remove swap file

Purpose

If the system uses a swap file instead of a swap partition, it should be removed to fully disable swap.

Syntax

rm -f <swap_file>

10.5 Validate swap is fully disabled

Purpose

Confirm that swap is disabled at runtime and will remain disabled after reboot.

Syntax

free -h

Expected output sample

              total        used        free      shared  buff/cache   available
Mem:           32Gi        4.1Gi       22Gi        120Mi        5.9Gi        27Gi
Swap:            0B          0B          0B

The Swap row must show 0B across all columns.


Purpose

Ensure /etc/fstab contains no invalid entries that could cause boot failures.

Syntax

mount -a

Expected output

(no output)

Any output here indicates a configuration error that should be fixed before rebooting.

The output should show 0 swap.

If swap is still present, do not continue — fix it first.


Configuring time synchronization (NTP)

Why time synchronization matters

In a Kubernetes cluster, time drift can cause:

  • certificate validation failures

  • leader election problems

  • confusing logs and debugging issues

All nodes must agree on time.

Configure chrony (closed or controlled environments)

💡
This configuration block is optional and only to be used in controlled or closed environments given that you have a private NTP available. If no NTP is available, then you’ll have to synchronize time on all your nodes manually.

We’ll be using chrony as the NTP server and client in our setup, make sure you have chronyd installed on your machine or you can install with sudo dnf install chronyd (RHEL flavors) or sudo apt install chronyd (Debian/Ubuntu flavors)

Edit the chrony configuration:

vim /etc/chrony.conf

Comment out the default pool ONLY if it is not accessible in your network premises:

This is a sample entry; your file may differ depending on your distro.

# pool 2.rhel.pool.ntp.org iburst

Add your NTP server:

server <Your NTP server> prefer

Example:

server 192.168.1.1 prefer

Enable and start chrony:

systemctl enable --now chronyd

Verify time sync

Run:

timedatectl
chronyc sources

Confirm:

  • NTP is enabled

  • A valid time source is selected


Preparing networking for Kubernetes

Firewall decision (important context)

For clarity and learning purposes, this guide intentionally disables the firewall and assumes a trusted internal network. This avoids hidden networking issues during setup.

Hardening and firewall rules can be added later once the cluster is stable.


Configure networking services

Stop and disable the firewall, enable required services, and ensure DNS consistency:

systemctl disable firewalld
systemctl stop firewalld

Enable iptables and systemd dns resolver, create a symlink for dns resolver entries:


systemctl enable --now iptables ip6tables systemd-resolved

ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf

Why this matters:

  • Kubernetes relies heavily on DNS

  • Inconsistent resolvers cause subtle failures

  • Node-to-node traffic must not be blocked


Verify networking readiness

Check:

  • Nodes can ping each other by IP and hostname

  • /etc/resolv.conf points to systemd-resolved

  • DNS resolution works consistently

Do not proceed until networking is clean.


Final system checks before Kubernetes

Before moving on, verify all of the following:

  • Swap is disabled

  • Storage mounts are present and persistent

  • Time synchronization is active

  • Firewall is disabled

  • DNS resolution works

  • Node has been rebooted after changes

If any check fails, fix it now — not later.


What’s next

In Part 2, we will install and configure the container runtime (containerd) along with essential node-level tooling. This is the foundation Kubernetes will run on, and correctness here is critical.