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.
Before you start
Section titled “Before you start”You need:
- A Frostyard bootc image. Every image built on Base has
systemd-sysextandfrostyard-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
/varfor 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/rancherif 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.
Enable K3s
Section titled “Enable K3s”See what Updex found, then enable the feature and download it now:
updex features list --component=k3ssudo updex features enable k3s --nowk3s 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:
systemd-sysext statussystemctl status k3s.service k3s-agent.servicesystemd-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.serviceruns unless/etc/default/k3s-agentexists.k3s-agent.serviceruns when/etc/default/k3s-agentis 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.
Use K3s
Section titled “Use K3s”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:
sudo tee /etc/default/k3s-agent >/dev/null <<'EOF'K3S_URL=<server-url>K3S_TOKEN=<token>EOFK3S_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:
sudo systemctl stop k3ssudo systemctl start k3s-agentsystemctl status k3s-agentThe 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.
Update and remove
Section titled “Update and remove”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:
sudo updex features disable k3s --now --forceUpdex 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
Section titled “Roll back”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.
Troubleshooting
Section titled “Troubleshooting”| 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. |
What ships
Section titled “What ships”| 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.
See also
Section titled “See also”- Docker system extension
- Manage system extensions
- System extension model
- Updex v2.1.3 command reference
Sources
Section titled “Sources”Reviewed 2026-10-27; Updex facts reviewed 2026-10-30. snosi commit
b2858ca0222814c395cf8248db98f3e096d57dc9;
Updex v2.1.3 commit
2661e987faac986f2594d1d9bbbadf1bf3053f5b.
mkosi.images/k3s/mkosi.confmkosi.images/k3s/mkosi.postinst.chrootmkosi.images/k3s/required-paths.txtmkosi.images/k3s/mkosi.extra/usr/lib/systemd/system/k3s.servicemkosi.images/k3s/mkosi.extra/usr/lib/systemd/system/k3s-agent.servicemkosi.images/k3s/mkosi.extra/usr/lib/systemd/system/k3s-sysext-setup.servicemkosi.images/k3s/mkosi.extra/usr/lib/systemd/system/multi-user.target.d/10-k3s.confmkosi.images/k3s/mkosi.extra/usr/lib/systemd/system-preset/40-k3s.presetmkosi.images/base/mkosi.extra/usr/lib/sysupdate.k3s.d/k3s.featuremkosi.images/base/mkosi.extra/usr/lib/sysupdate.k3s.d/k3s.transfershared/download/sysext-checksums.jsondocs/design/sysexts.md- Updex v2.1.3
README.md - Updex v2.1.3
updex/features.go - Updex v2.1.3 release
- Updex
docs/adr/0019-apply-merged-sysusers-and-tmpfiles-on-a-live-refresh.md(a live refresh runssystemd-sysusersandsystemd-tmpfiles --create; when the last refresh fails, the merge state is unknown; a refresh by hand does not runsystemd-sysusers) src/data/extensions.json- snosi issue #1100: k3s upholds a unit whose condition always fails
- Murbella’s live check of the role switch (frostyard-org#21 review)
docs/adr/0016-automatic-sysext-updates-apply-at-next-boot.md