Tailscale running natively on ZimaOS — sharing a sysext module

Hey folks,

I’ve been running ZimaOS on a ZimaCube for a while as my main homelab box, and one thing that always bugged me was getting Tailscale onto it cleanly. The Docker container works, but it runs with --tun=userspace-networking, which means no real subnet-router and a slightly weird networking story. I wanted Tailscale to feel like a first-class citizen on the host the same way it does on a regular Linux
server — systemctl status tailscaled and done.

Since ZimaOS has a read-only root and no package manager, the obvious “just apt install” path isn’t there. But I noticed ZimaOS is Buildroot-based, and Buildroot already has an official Tailscale recipe — it builds the daemon, drops it into /usr/bin, installs the systemd unit, etc. The natural Buildroot equivalent that doesn’t require rebuilding the whole ZimaOS image is systemd-sysext, which is what
i did already in the past - Zima_cron for example. Third-party sysexts are accepted in the Mod-Store too.

So I packaged Tailscale exactly the same way the upstream Buildroot recipe does — same paths, same symlinks, same systemd unit shape — but as a runtime sysext extension instead of a kernel-image change. State goes under /DATA/AppData/tailscale/ (because /var/ is tmpfs on ZimaOS, otherwise you’d lose your auth on every reboot). One file gets dropped into /var/lib/extensions/, systemd-sysext refresh, and
you’re done.

Repo: GitHub - chicohaager/zimaos-tailscale-sysext: Tailscale as a systemd-sysext for ZimaOS. Mirrors the upstream Buildroot recipe — no Docker, full subnet-router/exit-node support · GitHub

Quick install on the host:

git clone GitHub - chicohaager/zimaos-tailscale-sysext: Tailscale as a systemd-sysext for ZimaOS. Mirrors the upstream Buildroot recipe — no Docker, full subnet-router/exit-node support · GitHub
cd zimaos-tailscale-sysext
sudo ./install.sh
sudo tailscale up

A few things worth mentioning upfront:

  • It survives reboots and ZimaOS updates. /var/lib/extensions/ is a bind-mount onto the ext4 partition on ZimaOS (despite the /var prefix, which threw me at first), and systemd-sysext.service re-merges the extension at boot. After a ZimaOS upgrade, just re-run install.sh and your auth state is still there.
  • The installer rebuilds the .raw from the official Tailscale static tarball (pkgs.tailscale.com) and verifies the SHA256 against Tailscale’s own published hash. No mystery binaries.
  • One real limitation, fully honest: the ZimaOS kernel image doesn’t enable CONFIG_IPV6_MULTIPLE_TABLES (and a few related flags). Tailscale notices this at startup and auto-disables IPv6 tunneling — IPv4 mesh, subnet-router and exit-node all work fine, but IPv6-over-tailnet doesn’t. There’s nothing a userspace module can do about that; the kernel has to be rebuilt by IceWhale. I’ve drafted a
    feature-request body in the repo (mod-store/ICEWHALE_KERNEL_REQUEST.md) — if a few of you upvote it once it’s filed, that helps prioritize a fix.

There’s also a Mod-Store PR open in parallel — once that gets merged, this becomes a regular 1-click install in the ZimaOS UI alongside the other community modules. (For context: I also maintain the chicohaager/cron module that’s already in the Mod-Store, so this is the same delivery mechanism.)

Verified working on ZimaOS v1.6.1 / kernel 6.12.25 / ZimaCube. Should be fine on ZimaCube Pro too, and on ZimaBoard if you set ARCH=arm64 (haven’t tested ARM myself yet — would love confirmation if anyone has one).

Anyway — figured I’d share in case anyone else has wanted real Tailscale on the host without the Docker workaround. Happy to answer questions or take feedback (here or via GitHub issues), and if anyone runs into something the installer doesn’t handle gracefully, please tell me.

Holger / Lintuxer/Chicohaager

9 Likes

This is actually a very clever approach.

Using systemd-sysext instead of trying to modify the ZimaOS root directly makes a lot of sense for an immutable-style system like ZimaOS. Also nice to finally see proper subnet-router and exit-node support without relying on the Docker userspace networking workaround.

I also appreciate the honest explanation about the IPv6 limitation being kernel-related and not something userspace can fix. That kind of transparency helps a lot.

Really good community contribution overall, and honestly this is the kind of thing that shows how much potential ZimaOS has when advanced users start building proper native integrations around it.

Great work sharing this.

2 Likes

This is a very useful extension…

But how will this approach persist the following settings mentioned on tailscale docs and your repo after reboot.

echo ‘net.ipv4.ip_forward = 1’ | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo ‘net.ipv6.conf.all.forwarding = 1’ | sudo tee -a /etc/sysctl.d/99-tailscale.conf

Also… the tailscaled is not starting after reboot

Thanks for the report — reproduced on my own ZimaCube.

Quick clarification on the two points though, since they turn out to be different things:

tailscaled not starting after reboot — confirmed, real bug. Root cause: on ZimaOS, multi-user.target resolves its WantedBy= symlinks before systemd-sysext.service finishes merging the extensions, so tailscaled.service (which lives inside the sysext) isn’t visible yet and is never scheduled. No log, no error, just inactive (dead).

sysctl settings persistence — actually fine on ZimaOS. /etc is an overlayfs backed by persistent ext4 (/dev/nvme0n1p7/mnt/overlay/upper_etc), so /etc/sysctl.d/99-tailscale.conf does survive reboots, and systemd-sysctl.service re-applies it on each boot. Worth noting that those two sysctl lines are only needed if you’re using the host as a subnet router or exit node — not for plain mesh-VPN client mode.

Fix for the real bug (tailscaled boot) is ready and verified locally: a small tailscaled-watchdog.service in /etc/systemd/system/, modeled on the workaround ZimaOS’s own cron.raw ships. v1.0.1 in a day or two after a final clean-install test. Will ping here when tagged.

Fixed in v1.0.1 — thanks again @ntngblcrypt for catching this.

Cause, as suspected above: the boot-order race. multi-user.target resolves
its WantedBy= symlinks before systemd-sysext.service has merged the overlay,
so the in-sysext tailscaled.service doesn’t exist yet at that moment and is
never scheduled.

The fix uses the same approach as my ZimaOS’s cron.raw module: a small
tailscaled-watchdog.timer + oneshot service installed in /etc/systemd/system/
— the persistent root, present from early boot, outside the sysext. The timer
fires ~15 s into boot and starts tailscaled once the overlay is guaranteed
merged.

Verified on a real reboot: tailscaled now comes up by itself, exactly 15 s
after boot.

To update, just re-run install.sh (git pull first if you cloned the repo) —
it’s idempotent, adds the watchdog, and keeps your auth state. No need to
uninstall first — and better not to, since uninstall.sh logs the node out of
your tailnet.

1 Like

This is honestly such a great example of proper engineering and proper community support.

You didn’t just patch the issue, you actually explained the root cause clearly so people can understand what is happening under the hood on ZimaOS with sysext boot timing and service ordering.

The watchdog/timer approach is also a really elegant fix because it works with the appliance-style nature of ZimaOS instead of fighting against it.

Posts like this are incredibly valuable long term because they slowly build real technical knowledge around the ZimaOS ecosystem for everyone else learning alongside it.

Really appreciate you sharing both the problem and the reasoning behind the solution.

2 Likes

This is fantastic and exactly what I’m looking for. I’m currently looking to switch to ZimaOS from another similar product, and I need Tailscale installed at the host level so the host can mount my network drives that are on my Tailscale network. This is exactly the solution I need, and it looks to be a proper one that’s not a bunch of hobbled together hacks. Thank you for putting this together.

2 Likes

Script updated because IPv 6 ist supported now by Icwhale
Install:

cd /tmp
git clone https://github.com/chicohaager/zimaos-tailscale-sysext
cd zimaos-tailscale-sysext
sudo ./install.sh
sudo tailscale up