Tailscale
The Tailscale extension adds the Tailscale daemon and CLI to a Frostyard
bootc image, so the host itself can join your tailnet. The node identity lives
in /var, so it survives updates. Every image carries the extension’s
definition, switched off. The extension itself is not in the image: Updex turns
it on and downloads it with one command.
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. Checks that change
nothing run without
sudo. - Network access to
https://repository.frostyard.org/ext/tailscale/and to your tailnet’s coordination server. - A tailnet account, and the right to add this host to it.
- A plan to back up
/var/lib/tailscaleif this node must keep its identity across a reinstall.
Tailscale has no published test results on Frostyard images yet. Try it on a machine you can spare first.
Choose Tailscale
Section titled “Choose Tailscale”Use the extension when the host itself must be a node on your tailnet. It
gives you the standard tailscaled daemon and tailscale command. There is
no second client, relay, or self-hosted control server in the catalog.
The extension does not give you a tailnet or an identity provider account. It is not a sidecar for containers, and your containers do not get their own tailnet identities.
Enabling the extension turns on the daemon. It does not authorize the host.
On a new installation, the host joins the tailnet only after tailscale up
and your authorization. A
host with a restored node identity can reconnect without a new authorization.
Review your tailnet access rules before you authorize the host.
Enable Tailscale
Section titled “Enable Tailscale”See what Updex found:
updex componentsupdex features list --component=tailscaletailscale appears as a component, with its feature disabled.
Enable Tailscale and download it now:
sudo updex features enable tailscale --nowUpdex downloads the image, checks its checksum and signature, links it, and
refreshes the merged extensions. A missing or bad signature stops the command;
Updex never installs an unsigned image. Without --now, Updex only marks the
feature enabled, and the next update downloads it.
Enabling writes one file in /etc:
/etc/sysupdate.tailscale.d/tailscale.feature.d/00-updex.conf.
Check that Tailscale is merged and the daemon is running:
systemd-sysext statussystemctl status tailscaled.servicesystemd-sysext status lists tailscale, and tailscaled.service is
active. The daemon must be running before tailscale up.
A merge does not prove that setup completed. At boot, reload-sysext.service
runs systemd-sysusers, systemd-tmpfiles --create --graceful, a
systemd-sysctl replay of the inotify, fs.aio-max-nr, and kernel.keys
limits, and systemctl daemon-reload after the merge. A refresh by Updex v2.1.3
and later also runs systemd-sysusers and systemd-tmpfiles --create. After an
interactive --now merge, the systemd-sysctl replay waits for the next boot,
and systemd may not have loaded the new units yet. If the daemon is not active, reboot and check again.
Then connect the host to your tailnet:
sudo tailscale upFinish the sign-in your tailnet asks for. Wait until it completes before you rely on the host’s tailnet address.
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 run
the two checks above again.
Update
Section titled “Update”See whether an update exists:
updex features check --component=tailscaleThis changes nothing. It shows one row for each enabled feature. A feature
you have not enabled has no row, so a missing row does not mean “up to date”.
If Updex cannot fetch or verify a manifest, that row shows UPDATE=error and
the command exits non-zero. Treat an error row as unknown, not as up to date.
Install the newest version:
sudo updex features update --component=tailscaleUpdex downloads and verifies the new image, switches the link, deletes older
versions, and refreshes the merge. Add --no-vacuum to keep older versions.
To see what would change, without changing anything:
sudo updex --dry-run features update --component=tailscaleThe refresh swaps /usr under the running system. The daemon can restart,
and open sessions and connections can drop. If you reach other machines
through this host, update in a maintenance window.
Once you enable Tailscale, it updates on its own: new versions are staged and become
active at the next boot. To opt out for the whole host, run
sudo systemctl mask --now updex-update.timer. See
Automatic updates.
To take one update at the next boot without the timer, run
sudo updex features update --no-refresh --component=tailscale, then reboot.
Roll back
Section titled “Roll back”Roll back with sudo updex features rollback tailscale. It takes effect at the next boot. Updex then holds updates until a newer version is published. To stop future updates instead, see below. To remove the extension, use the remove command below. The node identity in /var/lib/tailscale stays either way. See Roll back.
Stop updates or remove
Section titled “Stop updates or remove”Stop future updates:
sudo updex features disable tailscaleThis only sets Enabled=false. Tailscale stays merged, the host stays on the
tailnet, and the link /var/lib/extensions/tailscale.raw stays. The next boot merges the version that the link names. LINKED in
updex features list shows it. To go back to the version you run now, roll
back first. Updex and the timer skip it from now on.
Remove Tailscale now:
sudo updex features disable tailscale --now --forceThis unmerges all extensions on the host, deletes the Tailscale image, and merges the rest again. A remote session through the tailnet can drop immediately. Make sure you have another way in, stop or drain your workloads, and reboot when the command finishes.
Without --force, Updex refuses while Tailscale is merged:
Extension tailscale (version ...) is active. Removing requires --force and a reboot to take effect.
Removal takes away the daemon and the CLI, and the host leaves the tailnet.
The node identity in /var/lib/tailscale stays. Keep it or delete it on
purpose:
- Restore
/var/lib/tailscaleon a reinstalled host to keep the node identity. A still-authorized node reconnects without a new authorization. - Without that state, the host is a new node. Run
tailscale upand authorize it.
Enabling Tailscale again does not by itself bring the host back on the tailnet. The daemon, the image, and the node’s authorization are separate.
If the refresh at the end fails, the remaining extensions may stay unmerged. Run
sudo systemd-sysext refresh, or reboot. If the output says
activation did not finish, they are merged, but their users and groups can be
missing: reboot.
Special cases
Section titled “Special cases”tailscaled.service is a system service, not a per-user one. The preset
enables it, and the 10-tailscale.conf drop-in starts it at boot after the
merge. A preset alone cannot do that for a unit that arrives in an extension.
The daemon reads /etc/default/tailscaled and will not start without it. The
extension copies its default file there when it is missing. Your edits to it
survive reboots and updates.
Restoring /var/lib/tailscale restores the node’s identity on the network.
Check the node in your tailnet before you rely on it.
Subnet routers, exit nodes, MagicDNS, and access rules are not covered here. Set them up as separate choices, and review them before you expose the host.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Check | Fix |
|---|---|---|
| Tailscale is not listed | updex features list --component=tailscale and updex components |
If tailscale is listed but disabled, enable it. If it is missing, the image may be older than named components. |
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. |
| The daemon does not start | systemctl status tailscaled.service and its journal |
Check that /etc/default/tailscaled exists. The daemon needs it. |
| The host is not on the tailnet | systemctl status tailscaled.service |
Check the service first. If the node key expired or an administrator removed the node, run sudo tailscale up and authorize the host again. |
| A manifest or signature check fails | updex features check --component=tailscale |
Check the network and the published manifest. Never turn verification off. |
| Remote access dropped during an update | The journal shows the daemon restart after the /usr refresh |
Update in a maintenance window. Keep another way into the host. |
| The host is offline after removal | systemd-sysext status and systemctl status tailscaled.service |
Enable Tailscale again. A still-authorized node reconnects without a new authorization. |
Do not repair the extension with apt. If a merged image is missing files,
the fix is a corrected extension build.
What ships
Section titled “What ships”| Item | What you get |
|---|---|
| Package | tailscale; the image file name carries its version |
| Image file | tailscale_<version>_<os>_<arch>.raw.zst; .raw.xz, .raw.gz, and .raw also work |
| Download | From https://repository.frostyard.org/ext/tailscale/, signature required (Verify=true), into /var/lib/extensions.d/ |
| Commands | /usr/bin/tailscale and /usr/sbin/tailscaled |
| Service | tailscaled.service, enabled by 40-tailscale.preset and started at boot by 10-tailscale.conf |
| Configuration | A default /etc/default/tailscaled |
| State | /var/lib/tailscale: the node identity and daemon state |
The active image is the link /var/lib/extensions/tailscale.raw. It points
to the chosen file in /var/lib/extensions.d/. The transfer file names a
second link, CurrentSymlink=tailscale.raw, but Updex deletes that one
(/var/lib/extensions.d/tailscale.raw) when it installs.
Updex picks the image that matches your OS version (%w in the file name)
and your architecture (%a). The catalog lists versions, not architectures,
so it does not tell you which architectures exist.
An extension cannot ship files in /etc. The extension keeps the packaged
/etc/default/tailscaled in /usr/share/factory/etc, and tmpfiles.d copies
it into /etc when the file is missing. That is why the daemon can start
right after a merge.
See also
Section titled “See also”- Snow image
- Docker system extension
- Incus system extension
- System extension model
- nbc to bootc migration runbook
- 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/tailscale/mkosi.confmkosi.images/tailscale/mkosi.finalizemkosi.images/tailscale/required-paths.txtmkosi.images/tailscale/mkosi.extra/usr/lib/systemd/system-preset/40-tailscale.presetmkosi.images/tailscale/mkosi.extra/usr/lib/systemd/system/multi-user.target.d/10-tailscale.confmkosi.images/tailscale/mkosi.extra/usr/lib/tmpfiles.d/tailscale.confmkosi.images/base/mkosi.extra/usr/lib/sysupdate.tailscale.d/tailscale.featuremkosi.images/base/mkosi.extra/usr/lib/sysupdate.tailscale.d/tailscale.transfermkosi.images/base/mkosi.extra/usr/lib/systemd/system/reload-sysext.serviceshared/sysext/postoutput/sysext-postoutput.shdocs/design/sysexts.mddocs/nbc-to-bootc-migration.md- Updex PR #445: refuse removal of a merged extension
- Updex v2.1.3
updex/features.go - Updex v2.1.3
README.md - Updex v2.1.3
updex/install.go - Updex v2.1.3
sysext/manager.go - Updex v2.1.3 configuration reference
- Updex v2.1.3 SDK reference
- Updex v2.1.3 ADR 0007: daemon stages, never activates
- 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) - snosi#1189 and snosi#1193 (open on 2026-10-30): a refresh reloads systemd only when a merged extension sets
EXTENSION_RELOAD_MANAGER=1, and this extension does not set it yet src/data/extensions.jsonmkosi.images/base/mkosi.extra/usr/lib/systemd/system-preset/05-updex.presetdocs/adr/0016-automatic-sysext-updates-apply-at-next-boot.md