Proxmox LXC K3S Integration and Orchestration

The modern landscape of container orchestration is dominated by Kubernetes, a system of immense power and flexibility that is unfortunately notorious for its resource heaviness. For the home lab enthusiast, the edge computing engineer, or the developer seeking a local testing environment, the overhead of a standard Kubernetes distribution can be prohibitive. This is where k3s, the lightweight Kubernetes distribution created by Rancher Labs, becomes an essential tool. k3s is a fully certified distribution by the Cloud Native Computing Foundation (CNCF) that achieves its small footprint by stripping out legacy APIs, bundling containerd, and shipping as a single binary under 100MB. Unlike some alternatives, k3s is not a fork of Kubernetes; it preserves all core Kubernetes functionalities and remains close to stock Kubernetes while replacing the backing datastore to reduce bloat.

Running k3s within Linux Containers (LXC), specifically on platforms like Proxmox, offers a strategic advantage over traditional Virtual Machines (VMs). While VMs require a full guest operating system and dedicated hardware virtualization—which consumes significant CPU and RAM—LXC containers run natively on the host kernel. This elimination of the virtualization layer results in drastically lower resource consumption. For those who have struggled with tools like minikube, which can cause high CPU usage on the host and impair development, the LXC approach provides a more performant alternative. However, this efficiency comes with a trade-off: lower isolation between the cluster and the host, and a more complex initial configuration process to grant the container the necessary permissions to manage networking and nested virtualization.

Setting up a k3s cluster in an LXC environment is not merely about installation; it is an exercise in operational intuition. In professional production environments, such as those hosted on AWS, Kubernetes clusters are often abstracted away by platform teams. While this abstraction increases delivery speed, it creates a knowledge gap regarding how scheduling, networking, storage, and controllers actually function under the hood. By bootstrapping a multi-node cluster using a combination of Ubuntu VMs and LXC containers on Proxmox, a technician can bridge this gap. This process allows for the intentional breaking and debugging of the system, providing a deep understanding of the underlying mechanics required to maintain a production-grade environment.

Architectural Comparison of Lightweight Kubernetes Distributions

When selecting a local Kubernetes environment, several options exist, including KIND, minikube, and microk8s. While these tools are functional, k3s is often preferred due to its superior documentation and its ability to remain production-ready despite its small size. The choice between an LXC-based deployment and a VM-based deployment depends entirely on the priority of the administrator.

Feature LXC Deployment VM Deployment Minikube/KIND
Resource Overhead Extremely Low Moderate to High Variable
Isolation Level OS-level (Shared Kernel) Hardware-level (Hypervisor) Application/Container
Setup Complexity High (Requires Tweaks) Moderate Low
Kernel Access Direct via Host Virtualized Guest Kernel Virtualized/Containerized
Performance Native Speed Virtualization Overhead Virtualization Overhead

Preparing the LXC Host Environment

The foundation of a k3s installation in LXC is the selection of the base image and the initialization of the container manager. For a stable Kubernetes host, Debian is a recommended base system. On a system running LXD, the creation of the host machine is initiated with a specific launch command to pull the Debian 10 image.

lxc launch images:debian/10 k3s-lxc

Once the container is launched, the administrator must verify the status of the machine to ensure it is operational and accessible within the network.

lxc list

If the administrator needs to explore other available base images to determine if a different distribution might better suit their specific hardware or software requirements, they can query the image list.

lxc image list images:

Essential LXC Configuration Tweaks for Kubernetes

Standard LXC containers are designed to be restricted for security reasons. However, Kubernetes is a system designed to manage other containers. Because k3s needs to set up nested containers, it requires specific capabilities to manage networking configurations and create control groups (cgroups). Without these modifications, the k3s server will fail to initialize.

The configuration is modified using the edit command:

lxc config edit k3s-lxc

The following settings must be merged into the configuration block to enable the necessary functionality:

config:
linux.kernelmodules: iptables,ip6tables,netlinkdiag,nf_nat,overlay
raw.lxc: lxc.mount.auto=proc:rw sys:rw
security.privileged: "true"
security.nesting: "true"

The impact of these settings is profound:

  • linux.kernel_modules: This ensures that the host kernel loads the modules necessary for networking and the Docker overlay filesystem. Specifically, ip_tables and nf_nat are critical for Kubernetes service networking.
  • raw.lxc: Setting proc:rw and sys:rw allows the container to modify critical system files that Kubernetes uses to manage the node state.
  • security.privileged: This removes certain restrictions, allowing the container to perform operations that would normally be reserved for the host root user.
  • security.nesting: This is the most critical setting for k3s, as it allows the creation of nested cgroups, enabling the "container-within-a-container" architecture.

Network Validation and Kernel Module Verification

Before proceeding to the installation of the k3s binary, it is imperative to verify that the host system supports connection tracking. Kubernetes relies heavily on conntrack to manage network flows.

To verify this functionality on the host, run:

conntrack -L

If this command does not produce output, it indicates that the necessary kernel modules are not loaded. In such a case, the LXC configuration must be updated to include the xt_conntrack module.

lxc config edit k3s-lxc

Add xt_conntrack to the linux.kernel_modules list. This ensures that the network packets are correctly tracked across the virtual bridge, preventing connectivity drops between pods and services.

K3s Installation and Deployment Process

The installation of k3s can be performed by pulling the binary on the host and pushing it directly into the container, ensuring that the binary is placed in the correct execution path.

First, the binary is downloaded and made executable on the host:

curl -Lo k3s https://github.com/rancher/k3s/releases/download/v0.9.1/k3s
chmod +x k3s

Next, the binary is pushed into the container's local bin directory:

lxc file push k3s k3s-lxc/usr/local/bin/k3s

Once the binary is in place, the administrator must enter the container to perform internal system configurations. To enter the container as the root user:

lxc exec k3s-lxc /bin/bash

Inside the container, the installation of ca-certificates is mandatory. Skipping this step will result in image pulling failures, as the container will be unable to validate the SSL certificates of the image registries.

apt-get install -y ca-certificates

Furthermore, newer versions of k3s attempt to read from /dev/kmsg for logging purposes. Since /dev/kmsg is not typically present in an LXC container, a symlink must be created to redirect these requests to /dev/console.

echo 'L /dev/kmsg - - - - /dev/console' > /etc/tmpfiles.d/kmsg.conf

After these modifications, the container must be restarted to apply the kernel module changes and the symlink configuration:

lxc restart k3s-lxc

In some instances, a standard restart may not be sufficient, and a hard reboot of the container is required:

lxc exec k3s-lxc reboot

To test the installation, the k3s server can be started manually inside the container:

k3s server

Persisting the K3S Service

Running the server manually is useful for troubleshooting, but for a production-ready homelab, the service must start automatically upon container boot. This is achieved by modifying the systemd unit file.

First, the administrator must stop the manual process using Ctrl+C. Then, the systemd configuration is edited:

systemctl edit --force --full k3s.service

The administrator should paste the content of the k3s unit file and specifically remove the EnvironmentFile line, as this file may not be present or may point to an incorrect location in the LXC environment. Finally, the service is enabled and started:

systemctl enable k3s
systemctl start k3s

Host-to-Container Connectivity and Kubectl Configuration

To manage the cluster from the host machine without having to execute every command inside the container, a kubectl context must be established. This involves creating a DNS entry on the host and pulling the kubeconfig file.

First, identify the IP address of the LXC container:

lxc list k3s-lxc

Add this IP to the host's /etc/hosts file to allow DNS resolution:

<k3s-lxc-ip> k3s-lxc

Verification is done via a simple ping:

ping k3s-lxc

To configure kubectl on the host, the k3s.yaml file must be pulled from the container and modified to replace the local loopback address with the container's DNS name.

lxc file pull k3s-lxc/etc/rancher/k3s/k3s.yaml .
sed -i 's:127.0.0.1:k3s-lxc:;s:default:k3s-lxc:g' k3s.yaml

To merge this configuration into the existing KUBECONFIG file without overwriting other clusters, the following sequence is used:

KUBECONFIG=~/.kube/config:k3s.yaml kubectl config view --raw > config.tmp
mv config.tmp ~/.kube/config
kubectl config use-context k3s-lxc

Advanced Cluster Expansion and Tooling

The initial setup of a single-node k3s cluster in LXC is only the first phase of a complete Kubernetes deployment. To transform this into a fully functional environment capable of hosting real-world applications, several advanced components must be integrated.

The objective for a complete deployment includes the following layers:

  • MetalLB Load Balancer: In a cloud environment, the cloud provider provides a LoadBalancer service. In a bare-metal or Proxmox environment, MetalLB is required to provide a virtual IP that can be used to route external traffic into the cluster.
  • NGINX Ingress Controller: This acts as the entry point for HTTP and HTTPS traffic, managing the routing of requests to the appropriate services based on hostnames or paths.
  • Istio Service Mesh: For complex microservices architectures, Istio provides advanced traffic management, security through mutual TLS, and observability into the communication between services.
  • Kubernetes Dashboard: A graphical user interface that allows for the visual management of the cluster, providing a more intuitive experience than the command line for monitoring pod health and deployments.

This tiered approach allows the user to start with a minimal footprint and add complexity as their needs evolve. By utilizing a mix of Ubuntu VMs and LXC containers, the administrator can balance the need for high isolation (for critical master nodes) and high efficiency (for worker nodes).

Conclusion: Analysis of the LXC-based Kubernetes Paradigm

Implementing k3s within LXC containers represents a sophisticated compromise between the heavy overhead of full virtualization and the restrictive nature of application containers. The process reveals that while Kubernetes is designed to be agnostic of its underlying infrastructure, the actual implementation on a local hypervisor like Proxmox requires a deep understanding of Linux kernel modules, cgroups, and systemd service management.

The "hard way" of installing k3s—manually pushing binaries, configuring symlinks for /dev/kmsg, and tweaking privileged container settings—is not merely a hurdle; it is a learning mechanism. It forces the operator to confront the realities of the Linux networking stack and the specific requirements of the Kubernetes API. The transition from a managed cloud environment to a self-hosted LXC cluster transforms the user from a consumer of platform services into a platform engineer.

From a performance perspective, the LXC approach is demonstrably superior for resource-constrained hardware. By avoiding the "virtualization tax" of a VM, more of the host's RAM and CPU can be allocated to the actual workloads running within the pods. However, the trade-off in security is real. A privileged LXC container has a larger attack surface than a properly isolated VM. Therefore, this architecture is ideal for learning, development, and home automation, but should be approached with caution in multi-tenant production environments.

Ultimately, the synergy between Proxmox, LXC, and k3s creates a potent environment for rapid prototyping. The ability to quickly deploy, destroy, and recreate nodes using LXC templates, combined with the lightweight nature of k3s, enables a DevOps cycle that is significantly faster than that provided by traditional VM-based Kubernetes clusters.

Sources

  1. Running k3s on Proxmox: A Multi-Node Cluster with a VM and LXC Worker
  2. Kubernetes LXD Guide
  3. From Zero to K3S on Proxmox LXC Part 1

Related Posts