K3s cloud-init is surprisingly powerful because it lets you bootstrap entirely new nodes into your K3s cluster before they even know they’re supposed to be part of a cluster.

Let’s watch it in action. Imagine you’ve just spun up a new virtual machine, or an EC2 instance. It boots, and the cloud-init service kicks in. It reads a configuration file you’ve provided – typically via user data. This file is a YAML document.

Here’s a snippet of what that cloud-init config might look like for a new K3s agent node:

#cloud-config
runcmd:
  - curl -sfL https://get.k3s.io | K3S_URL=https://your_server_ip:6443 K3S_TOKEN=your_node_token sh -
  - systemctl enable k3s-agent
  - systemctl start k3s-agent

When this file is processed, cloud-init executes the runcmd directives. The first line downloads the K3s installation script and immediately runs it, injecting two crucial environment variables: K3S_URL pointing to your existing K3s server’s API endpoint and K3S_TOKEN which is the secret key that authorizes this new node to join the cluster. The subsequent lines ensure the K3s agent service is configured to start on boot and then immediately start it.

The problem K3s cloud-init solves is the manual, repetitive task of joining new nodes to a cluster. Traditionally, after provisioning a new server, you’d SSH into it, install K3s, and then run the agent join command. Cloud-init automates this entire process, making it seamless. You can provision a fleet of new servers, and as each one boots, it automatically configures itself as a K3s agent and registers with your control plane.

Internally, cloud-init is a widely adopted standard for early initialization of cloud instances. It’s designed to be distro-agnostic and works by inspecting a set of data sources (like user data, network metadata, or local config files) and then executing a predefined set of modules. For our K3s use case, the runcmd module is the workhorse, allowing us to execute arbitrary shell commands.

The exact levers you control are the contents of the cloud-config YAML file. You can add more than just the K3s installation. For example, you might want to pre-configure firewall rules, install additional packages, or even run custom setup scripts before K3s even starts.

Here’s a more comprehensive example for a K3s server node:

#cloud-config
packages:
  - ntp
runcmd:
  - |
    curl -sfL https://get.k3s.io | sh -s - --server \
      --cluster-init \
      --tls-san your_server_ip \
      --node-ip your_server_ip
  - systemctl enable k3s
  - systemctl start k3s
  - sleep 30 # Give K3s time to start before potentially checking status
  - k3s kubectl get nodes
write_files:
  - path: /etc/rancher/k3s/config.yaml
    permissions: '0644'
    content: |
      write-kubeconfig-mode: 0600

In this server example, we first install ntp to ensure time synchronization, which is critical for Kubernetes. The runcmd now uses sh -s - --server --cluster-init to set up this node as a new K3s server. We also explicitly set --tls-san and --node-ip to ensure the server’s certificate is valid for its IP address. We then enable and start the k3s service. A sleep is added to allow the service to initialize before we attempt a kubectl command to verify its status. Finally, write_files is used to configure K3s to write the kubeconfig with restricted permissions, a common security best practice.

The most surprising aspect of cloud-init for K3s, and often overlooked, is its ability to perform complex conditional logic or fetch dynamic data. While the examples above are sequential, you can use directives like bootcmd (which runs even earlier in the boot process) or integrate with cloud provider APIs within your runcmd scripts to, for instance, fetch the IP address of the control plane dynamically rather than hardcoding it. This allows for truly self-configuring infrastructure where nodes can discover their peers and configuration without manual intervention beyond the initial instance launch.

The next concept you’ll likely explore is managing K3s cluster configuration and secrets dynamically as your cluster scales.

Want structured learning?

Take the full K3s course →