NixBSD Image
omfreebdy normally installs the imperative way — pkg install, copy configs, enable services in rc.conf. The NixBSD image is the same desktop expressed declaratively: one Nix flake that pins every package, config file, script, and service, then cross-compiles the whole thing from a Linux box and boots it as a NixBSD virtual machine — a real FreeBSD 15 kernel and userland, managed NixOS-style.
No FreeBSD machine is touched to build it. nix build on Linux produces a bootable x86_64-unknown-freebsd image with the i3 desktop, the omfreebdy theme set, every user script, and a full CLI/dev toolchain baked in.

The Nix-built omfreebdy i3 desktop, rendered live on a FreeBSD 15 NixBSD VM — gruvbox wallpaper, i3 + status bar, and fastfetch reporting the FreeBSD kernel, all cross-built from a single declarative flake on Linux. Theme switched live with theme-set gruvbox.
What This Is
Section titled “What This Is”| Kernel | NB-K | FreeBSD 15.0-RELEASE | Real BSD kernel + userland, not Linux |
| Management | NB-M | NixBSD | NixOS-style declarative config on the BSD kernel |
| Desktop | NB-D | i3 / X11 | i3 + i3status + picom + rofi, all 18 themes |
| Build host | NB-B | Linux → FreeBSD | Cross-compiled; no FreeBSD machine needed |
| Render path | NB-R | Xvfb (software) | Headless X server — no GPU or DRM required |
| Output | NB-O | QEMU VM image | Boots straight to the desktop over VNC |
The imperative pkg-based install is still the primary way to run omfreebdy on real hardware. The NixBSD image is a parallel, reproducible build target — handy for VMs, CI, and proving the whole stack assembles from a single source of truth.
What Works
Section titled “What Works”The image isn’t a stripped-down demo — it reproduces omfreebdy’s actual install: every script in bin/, the full config tree, the user shell, services, and a real toolchain.
| Layer | Included |
|---|---|
| Window manager | i3, i3status, picom, rofi, xterm + st |
| Wallpaper | xwallpaper paints the catppuccin background on the Xvfb root |
| Themes | All 18 omfreebdy themes (catppuccin, gruvbox, everforest, kanagawa, …) |
| Scripts | The complete bin/ set — theme-set, theme-list, every helper |
| Configs | Seeded verbatim from config/ into ~/.config on first boot |
| CLI tools | btop, fastfetch, starship, tmux, zoxide, fzf, eza, bat, fd, jq, ripgrep, lazygit, gh, dust, … |
| Dev toolchain | Go, Python 3.11, Node 22, Ruby, GNU Make, ImageMagick |
| Login | Autologin getty → user shell → X session |
How To Change Themes
Section titled “How To Change Themes”Identical to the rest of omfreebdy — the theme scripts ship in the image:
theme-set rewrites the i3, i3status, picom, and rofi colors and reloads them — exactly as on a pkg-installed system. See the Themes guide for authoring your own.
Building It
Section titled “Building It”The build runs entirely on Linux. Two non-obvious pieces make the FreeBSD cross-compile work:
| Evaluator | CPP | cppnix | The rhelmot/nix fork — stock Nix infinite-recurses on the FreeBSD stdenv bootstrap |
| Cache | SUB | nixbsd attic | Pre-built FreeBSD bootstrap from the NixBSD project’s binary cache |
The flake also exposes .#omfreebdy-wayland-vm (the Hyprland profile). It builds and boots, but Hyprland needs a real GPU to render — see the journey below.
The Cross-Compile Journey
Section titled “The Cross-Compile Journey”Getting omfreebdy onto a FreeBSD kernel from a Linux builder meant teaching nixpkgs a handful of FreeBSD facts. None of these are omfreebdy bugs — the same software builds cleanly on real FreeBSD via pkg. The gap is purely nixpkgs’ Linux-centric cross-packaging.
The first target was the real Hyprland desktop. It cross-compiled completely — mesa/llvmpipe, aquamarine, Hyprland 0.53.1, foot, fuzzel, swaybg, polkit, seatd — and booted, reading its activation-seeded hyprland.conf and bringing up its full manager stack.

Hyprland booting on the FreeBSD-kernel VM — it builds every manager, then dies at GPU init.
Then it hit a hard wall: aquamarine’s only buffer allocator is GBM, which requires a DRM render node (/dev/dri/renderD*). FreeBSD 15 in QEMU has no virtio-gpu KMS driver, so there’s no /dev/dri at all — Cannot open backend: no allocator available. This isn’t a packaging bug; it needs real GPU hardware with drm-kmod. So for the VM we pivoted to omfreebdy’s i3/X11 profile, which software-renders under Xvfb with no GPU.
nixpkgs targets FreeBSD with the versionless triple x86_64-unknown-freebsd — the clang-standard NixBSD identity. That’s the platform’s identity; changing it would rebuild the entire package set. Most things accept it; the GNU toolchain’s config.gcc doesn’t (it extracts FBSD_MAJOR from the triple and aborts when there’s no version). A one-line patch defaults it to 15.
A scattering of packages pull deps that exist only on Linux (or are glibc-compat shims). Each is a peripheral feature, not a core component — so the fix is to drop the dep, not the package:
- i3status — drop the libnl (network) and pulseaudio (volume) modules; time/cpu/disk/load stay.
- i3 — drop the Perl
X11::XCB/Inline::Chelper-script stack; the WM binary doesn’t need it. - scrot — drop libbsd (a glibc
strlcpyshim; FreeBSD libc already has it). - plocate — drop systemd (only used to place a
.timerunit). - feh — use plain imlib2, supply a
strverscmpimplementation, re-expose BSD extensions with-D__BSD_VISIBLE=1. - fftw (via ImageMagick) — drop its optional Fortran interface; the C FFT library needs no Fortran.
- btop / dust / bat — add FreeBSD’s
libproc/libgeom. - gh — disable cgo. ripgrep — skip the install-check that runs the target binary on the Linux builder.
The goal was to run omfreebdy’s actual config unchanged — fix NixBSD, don’t fork the desktop:
/bin/shmust be ash — NixBSD points/bin/shat bash, but FreeBSD’swordexp(3)(used by i3’sinclude,exec, andstatus_command) needs the real ash shell. Setenvironment.binshtofreebsd.bin’s sh.- Locale data — NixBSD defaults
LANG=en_US.UTF-8but ships no/usr/share/locale, sosetlocale()fails → i3status exits and rofi segfaults. PointPATH_LOCALEatfreebsd.locales. ~/.cache— rofi segfaults writing its run/drun history to a missing cache dir; create it and setXDG_CACHE_HOME.
Deferred
Section titled “Deferred”A few things build on FreeBSD via pkg but aren’t cross-packaged in nixpkgs yet. They’re tracked, not abandoned — the genuine frontier is cross-compiling compilers.
| Item | Why deferred |
|---|---|
| gfortran / OpenBLAS | The cross-GCC nixpkgs builds is glibc-wired — no FreeBSD libc. The config.gcc triple patch is a correct starting point but not sufficient on its own. |
| flang | LLVM Fortran cascades into MLIR cross-build scope surgery (cross mlir-tblgen). |
| rustc | Bootstrap stage runs a target binary on the Linux builder. |
| neovim | Runs the freshly-built nvim during its own build. |
None of these block the desktop or the CLI/dev toolchain. They’re follow-ups in nixpkgs’ FreeBSD cross-compilation support — the software itself runs fine on FreeBSD today.