Skip to content

K3s

The k3s extension adds a single-binary Kubernetes node to a Frostyard bootc image. Use it when the host must be a Kubernetes server, or must join an existing cluster as an agent. Every image carries the extension’s definition, switched off. The extension itself is not in the image: Updex turns it on and downloads it.

You need:

  • A Frostyard bootc image. Every image built on Base has systemd-sysext and frostyard-updex.
  • Root access to enable, update, and remove the extension.
  • Network access to https://repository.frostyard.org/ext/k3s/, and to the cluster server if this node will join as an agent.
  • Free space in /var for the image and for the cluster state.
  • A server URL and a token, if this node will be an agent.
  • A copy of /var/lib/rancher if the cluster must survive a reinstall.

Both units try to load the br_netfilter and overlay kernel modules before k3s starts. A failed load does not stop the unit.

K3s has no published test results on Frostyard images yet. Try it on a machine you can spare first.

See what Updex found, then enable the feature and download it now:

Terminal window
updex features list --component=k3s
sudo updex features enable k3s --now

k3s appears as a component, with its feature disabled. The enable command downloads the image, checks its checksum and signature, links it, and refreshes the merge.

Check that the image is merged and that one unit runs:

Terminal window
systemd-sysext status
systemctl status k3s.service k3s-agent.service

systemd-sysext status lists k3s. On a fresh node k3s.service is active and k3s-agent.service is inactive. If neither unit runs yet, reboot: at boot, multi-user.target starts k3s-sysext-setup.service, which pulls in the unit that matches the role.

The two units select the role at start time. Both are enabled. k3s-sysext-setup.service pulls in both, and their conditions pick one:

  • k3s.service runs unless /etc/default/k3s-agent exists.
  • k3s-agent.service runs when /etc/default/k3s-agent is not empty.

A fresh node starts a standalone server, as the upstream installer does.

If enable --now exits with a refresh error (refresh_error in JSON), the download worked, but the activation did not. If the output says activation did not finish, the extension is merged, but users and groups can be missing: reboot. Otherwise the merge state is unknown: reboot. A sudo systemd-sysext refresh by hand does not create users and groups. Then check again.

Server options go in /etc/rancher/k3s/config.yaml, which k3s reads itself, or in /etc/default/k3s, which the unit reads as an environment file. The extension ships no defaults. Both files live in /etc, so they survive reboots and updates.

To turn a fresh node into an agent, create the agent file:

Terminal window
sudo tee /etc/default/k3s-agent >/dev/null <<'EOF'
K3S_URL=<server-url>
K3S_TOKEN=<token>
EOF

K3S_TOKEN_FILE= also works in place of K3S_TOKEN=.

From now on the file keeps k3s.service from starting. Nothing switches the role on a running node by itself, and the running server does not stop. Reboot, and the node starts as an agent. Or stop the server and start the agent by hand:

Terminal window
sudo systemctl stop k3s
sudo systemctl start k3s-agent
systemctl status k3s-agent

The data from the server run stays in /var/lib/rancher/k3s/server. A node that ran as a server is not tested as an agent: start agents on fresh nodes when you can.

The binary is /usr/bin/k3s. /usr/bin/kubectl and /usr/bin/crictl are relative links to it, so they resolve in the merged /usr.

There is no ctr link. Docker’s containerd ships a real /usr/bin/ctr, and two merged extensions that claim one path shadow each other. Use k3s ctr for this node’s containerd.

All state is in /var/lib/rancher.

An update refreshes the merged /usr while k3s runs. Drain the node before you update by hand. The cluster state in /var/lib/rancher stays.

Remove K3s now:

Terminal window
sudo updex features disable k3s --now --force

Updex unmerges all extensions on the host, deletes the k3s image, and merges the rest again. Drain the node first. Reboot when the command finishes. Without --force, Updex refuses while the image is merged. /var/lib/rancher stays: keep it for a later reinstall, or delete it yourself.

Roll back with sudo updex features rollback k3s. It takes effect at the next boot. Updex then holds updates until a newer version is published. To stop future updates instead, run sudo updex features disable k3s. /var/lib/rancher stays either way. See Roll back.

See Manage system extensions for the update check, the timer, and plain disable.

Symptom Check Fix
No k3s unit runs systemctl status k3s.service k3s-agent.service If /etc/default/k3s-agent exists but is empty, both conditions fail. Put K3S_URL= and K3S_TOKEN= in it, or delete it.
The node runs as a server, but it must join a cluster systemctl status k3s.service and ls /etc/default/k3s-agent Create the file with K3S_URL= and K3S_TOKEN=, then start k3s-agent.service and stop k3s.service.
ctr is missing, or belongs to another extension command -v ctr The extension ships no ctr link. Use k3s ctr.
enable --now exits non-zero after the download The output shows Error: sysext refresh failed (refresh_error with --json) If the output says activation did not finish, reboot. Otherwise reboot too: a refresh by hand does not create the extension’s users and groups. Then check systemd-sysext status.
Item What you get
Package k3s: a build-local deb around the upstream static binary, so dpkg resolves the version
Version The image file name carries the upstream k3s version, plus a +rN snosi revision when one is set
Image file k3s_<version>_<os>_<arch>.raw.zst; .raw.xz, .raw.gz, and .raw also work
Download From https://repository.frostyard.org/ext/k3s/, signature required (Verify=true), into /var/lib/extensions.d/
Commands /usr/bin/k3s, with /usr/bin/kubectl and /usr/bin/crictl as links
Services k3s.service for a server and k3s-agent.service for an agent, enabled by 40-k3s.preset. 10-k3s.conf upholds only k3s-sysext-setup.service, which pulls in the role units at boot
State /var/lib/rancher
Configuration None shipped: you write /etc/rancher/k3s/config.yaml, /etc/default/k3s, or /etc/default/k3s-agent

The build downloads the release binary and checks its SHA256. The version check follows the k3s stable channel, not GitHub’s latest release.

Each build checks that the binary, both links, both units, the setup unit, the preset, and the boot drop-in are present. A build that is missing one fails. The image is an overlay, so a later Base can break an older k3s image.

Reviewed 2026-10-27; Updex facts reviewed 2026-10-30. snosi commit b2858ca0222814c395cf8248db98f3e096d57dc9; Updex v2.1.3 commit 2661e987faac986f2594d1d9bbbadf1bf3053f5b.