nixnas

Your declarative NixOS — secure, RAM-resident, on a USB stick

nixnas packages your own NixOS config into a USB-bootable appliance: it runs from RAM, locks the boot chain end to end, keeps every generation for rollback, and connects to the storage you already have. You bring the declarative config — a local TUI writes your signed image.

nixnas — write your image, boot from RAM
$ nixnas                       # a local TUI over your own flake
[nixnas] LUKS passphrase: ********   (RAM-backed 0600 file, shredded after the build)
[nixnas] building .#imageScript — CachyOS kernel · LUKS f2fs-zstd store · lanzaboote-signed UKI
[nixnas] ✓ nixnas.raw  — your config, ~3.4 GiB, zstd-compressed on the stick
[nixnas] flashing → /dev/disk/by-id/usb-…

$ # boots reachable, data still locked — one passphrase over SSH:
$ ssh root@box nixnas-unlock   🔐 passphrase: ********
[  OK  ] store: passphrase-only · /nix on f2fs zstd:22 · root = tmpfs (RAM)
[  OK  ] your storage unlocked + imported + mounted — never formatted
box login: _

How it works

1

Bring your config

Your NixOS flake imports the nixnas module and sets a handful of nixnas.* values. Everything you actually run — services, containers, orchestrators, shares — is your own declarative NixOS.

2

Write your image

A local Rust TUI builds the personalised image on your machine, signs it with your own Secure Boot keys, and flashes the stick. Your secrets never leave.

3

Boot from RAM

Encrypted, headless, with a tmpfs root. Every disk needs your passphrase; TPM protects only the initrd SSH identity. A bad update? Boot the previous generation from the menu.

What nixnas does

Boot a Nix from USB, into RAM

From an 8 GB stick up. The root is a tmpfs (impermanence); only /nix and the ESP persist, and the closure is warmed into a compressed page cache — run-from-RAM without copytoram, updates intact.

Secure Boot, your keys

lanzaboote signs every Unified Kernel Image with your own PK/KEK/db. The Microsoft CA is not enrolled — no unsigned kernel runs. Paired with a firmware password and measured boot, it's evil-maid hardened.

Passphrase-only disks

The operator supplies a passphrase on every boot. No TPM token or auto-unlock path can release a disk key.

Headless remote unlock

No console? A per-device TPM-gated initrd SSH identity authenticates the channel for the mandatory passphrase. With no TPM, unlock locally or through IPMI.

Tuned for speed

The CachyOS kernel (x86-64-v3 + ThinLTO), pulled pre-built from a binary cache — performance without a local compile. f2fs zstd:22 keeps the slow stick small.

Declarative & rollback-safe

The whole appliance is your git-versioned NixOS. Every past generation stays bootable as its own signed UKI; the bootloader menu is the guaranteed rollback, with boot-counting on top.

Delivered updates, never self-reboots

Updates arrive through a nixdeploy receiver composed alongside nixnas. Nothing in nixnas reboots the box, so a headless box is never abandoned at a passphrase prompt.

Bring your own storage

Connect the storage you already have — any Linux filesystem and encryption. nixnas imports and unlocks it; it never creates, formats, or destroys it. The box boots even if a disk is missing.

Kind to the stick

Logs, /tmp, coredumps and swap live in RAM, so the USB takes ~no writes except updates — measured at 60 KiB for ~100 MiB of activity. Cheap sticks don't wear out.

Portable disks

Every disk carries a passphrase keyslot, so a disk pulled into another machine still opens. TPM protects only the remote unlock identity.

One passphrase, all your data

nixnas-unlock opens your LUKS members serially — the first entry is cached in the kernel keyring and opens the rest, imports the pools, and starts everything gated on them. One entry per boot, for the whole set.

Thin by design

nixnas owns boot, crypto, the USB store, and kernel packaging — nothing else. Your services, containers, k3s, shares, VMs — whatever you run — are your own NixOS, declared alongside.

Architecture

   USB stick  (8 GB+, write-isolated)
     ESP     signed UKIs        -  Secure Boot (your keys) + rollback
     store   LUKS2 + f2fs-zstd  -  the nix store = your OS
       |
       |  unlock once:  passphrase  (local/IPMI or TPM-gated initrd SSH)
       v
   root = tmpfs (RAM)  -  /nix warmed into compressed RAM
       |
       +--->  your declarative config in git   (services, containers, k3s - you bring)
       |
       +--->  your Linux storage               (any FS + encryption; imported, never formatted)

Impermanence

The root is a tmpfs; only /nix and the ESP survive a reboot. The OS runs from RAM — the slow stick is spared, runtime state lives on your storage.

You bring the rest

nixnas is the boot + crypto + store + kernel mechanism. Your workloads — k3s, containers, file shares, VMs — are plain NixOS in your own git, built into the same image.

Import-only

nixnas unlocks and mounts your existing storage. It never creates, formats, or destroys it — the only device it ever partitions is the USB stick.

Get started

Scaffold your host

nixnas is the public core; your real disks, keys and config live in your own flake that imports it:

nix flake init -t github:corbet-nix/nixnas#host

Fill in your nixnas.* values in host.nix — storage, SSH keys, the USB device — and add whatever else you run as normal NixOS.

Write & flash your image

The Rust TUI builds the personalised image on your trusted machine and flashes the stick:

cd tui && cargo run
# → builds .#image, prompts the passphrase,
#   flashes via caligula

Requirements

A UEFI x86-64 box with TPM 2.0, an 8 GB+ USB stick, and your own storage (any Linux filesystem; LUKS members carry the chosen passphrase as a keyslot). Secure Boot needs your own keys enrolled + a firmware password.

Status. The passphrase-only LUKS image, TPM-gated initrd SSH identity, rollback, and update paths are VM-tested. Real-hardware gates remain: owner-key Secure Boot enrollment, firmware setup-password enforcement, TPM credential bootstrap, and physical main/rescue boots. Signed-version anti-rollback is not claimed. See the architecture notes.