Back
NixOS on the Steam Link: a home-built kexec module bypasses Valve's signed-kernel lock
SiTech AI Team3 წთ. საკითხავი

NixOS on the Steam Link: a home-built kexec module bypasses Valve's signed-kernel lock

George Huebner revived a 2018 Steam Link as an always-on ARM server. Because the box only boots Valve-signed kernels, he compiled a minimal kernel module that adds the missing kexec syscall and hands the boot over to a slimmed-down NixOS image.

George Huebner found a Steam Link in his closet, a 2018 flash-sale purchase that was still running, and decided the small ARM box, with Ethernet, Wi-Fi, Bluetooth and several USB ports, would make a handy always-on, low-power device. His 26 September 2026 write-up describes how he got NixOS running on it and which kernel-module trick made the switch possible.

The hard parts had already been mapped out by a reverse engineer who goes by fijam, whose notes the author credits. The obstacle: the device's bootloader only boots kernels signed by Valve, so a custom kernel cannot be launched directly. The workaround is to boot into the Valve-approved kernel and then kexec into the new one.

A syscall rebuilt as a kernel module

That plan runs into a second wall: the kernel Valve ships was not built with CONFIG_KEXEC enabled, so the kexec syscall is simply not there. The author gathered the relevant kexec source files and built them into a minimal kernel module, kexec_load.ko, which adds the missing syscall to the running system. Others have used the technique before, but most just download fijam's prebuilt module and kexec binary. Wary of loading kernel modules from the internet, Huebner compiled his own.

Cross-compiling for a 13-year-old kernel

Building the NixOS userspace, kernel and initrd is straightforward: pass the right system and crossSystem to lib.nixosSystem. Architecture was less simple. Valve's toolchain targets armv7a, but the equivalent crossSystem setting, armv7a-unknown-linux-gnueabihf, interacts badly with Nix's Go build plumbing, so he used the seemingly equivalent armv7l instead, cross-compiling from an aarch64 host. The module itself must be built against Valve's vendored fork of a 13-year-old kernel, version 3.8.13. It needed patches to compile with a modern GCC, and a full kernel build rather than just make modules_prepare: module versioning needs Module.symvers, and without it insmod rejects the result.

Flying blind, then trimming the fat

Verifying the handoff was awkward: the HDMI output does not work with the new kernel, and the author would not open the device to reach the UART header. He tested with a throwaway BusyBox initramfs that wrote a marker file to disk and rebooted to signal success, then switched to a NixOS initrd with the network enabled and a Netcat reverse shell to his laptop. Two details mattered: reset_berlin had to be added to the initrd's available modules so the USB drive could be read, and the older, non-systemd initrd had to be used. Once booting worked, the image still weighed 2.3 GB, so he cut documentation, fonts, XDG services, Nix itself and all but one firmware blob from the 1.8 GB linux-firmware package, stopping at 1.2 GB and calling the result good enough. He also notes that someone else recently vibed their way to a bootable NixOS config, but it mishandles reboots and leans on unnecessary binary blobs. The flake and module source are published with the post.

SSiTech

SiTech — AI-powered web development

We build fast, modern websites and bring AI into real business workflows. Have a project or a question? We'd love to help.