← Back · NextBSD Research · companion to Linux ABI mounts
apt wrapperHow a NextBSD user gets a Linux userland to run Linux programs. Type apt install google-chrome-stable. If no Linux root exists yet, NextBSD bootstraps one with debootstrap: Devuan 6 by default, with Debian or Ubuntu if you ask. It mounts it, then runs Debian’s own apt inside it with your arguments unchanged. This page covers which distro, how “latest” is chosen, what the wrapper does, and how the graphics stack lines up with NextBSD’s 6.12 DRM drivers.
The short answers.
--variant=minbase, into /compat/linux. It has the same Mesa 25.0.7 and libdrm 2.4.124 as Debian 13 trixie, built against kernel 6.12, an exact match for NextBSD’s drm-kmod 6.12-lts. It has no systemd at all, and systemd’s postinst is the one thing that has actually failed under the Linuxulator. Debian trixie minbase is an equally good second choice. Ubuntu LTS is opt-in.dpkg under the Linuxulator, and debootstrap mounts what it needs itself. It goes straight into the already-mounted root (launchctl bootstrap mounts it at every boot, #244); the wrapper then remounts proc, which debootstrap unmounts (mounts plan §3).nextbsd-linux, in NextBSD-userland, with /usr/bin/apt as a thin shim. apt runs inside a chroot, as root. Installed programs run directly through the Linuxulator’s alt root, as the linux-chrome and claude-code ports already do. There is no apt in FreeBSD base, ports or Darwin, so the name is free.# apt install google-chrome-stable
No Linux userland at /compat/linux.
Bootstrap Devuan 6 (excalibur) minbase for amd64 from http://deb.devuan.org/merged?
About 50 MB to download and 200 MB on disk. [y/N] y
… debootstrap output …
Linux userland ready: Devuan 6 (excalibur), amd64. Mounts up.
Reading package lists… ← Debian's own apt from here on
Later runs skip straight to apt. apt update, upgrade, install and remove behave exactly as on Debian, because they are Debian’s apt. apt’s own exit code is passed through unchanged.
The table below is as of 2026-09-21, from each archive’s dists/*/Release and Packages.
| Devuan 6 Excalibur | Debian 13 trixie | Ubuntu 24.04 noble | Ubuntu 26.04 resolute | |
|---|---|---|---|---|
| Status | stable 6.0 (2025-11-02) | stable 13.7; LTS 2028–2030 | LTS to 2029 | LTS 26.04.1 |
| Arches | amd64, arm64 | amd64, arm64 | amd64; arm64 via ports.ubuntu.com | same |
| Mesa / libdrm / built against | 25.0.7 / 2.4.124 / 6.12 (same binaries as trixie) | 25.0.7 / 2.4.124 / 6.12 | 24.0.5 (25.2.8 in updates) / 2.4.120 / 6.8 | 26.0.3 / 2.4.131 / 7.0 |
| systemd in a minbase root | none; sysvinit-core, eudev, elogind | none in minbase (init/systemd are important, not required) | in the default set; triggers the port’s postinst workaround | same |
| debootstrap script in the FreeBSD port | no; vendor Devuan’s ceres script (excalibur and stable are symlinks to it) | trixie, stable | noble | resolute |
| Archive keyring on FreeBSD | fetch devuan-keyring .deb | fetch debian-archive-keyring .deb (the debian-keyring port is the developer keyring, not the archive one) | security/ubuntu-keyring | same |
| Chrome .deb | all deps resolvable (libudev1 from eudev); install unverified | Google targets it; amd64 + arm64 | yes | yes |
Why Devuan by default.
merged archive re-exports Debian’s binaries and overrides only the init, udev and logind family, so the graphics pairing is exactly trixie’s.Debian trixie minbase is the zero-vendoring alternative. It has the same graphics stack, and minbase is systemd-free at bootstrap; systemd only arrives if a later apt install pulls it in, and Chrome’s dependencies don’t. Ubuntu stays opt-in. 26.04’s Mesa was built for kernel 7.0, which is probably fine on 6.12 but untested, and its systemd-heavy default set triggers the known stage-2 failures.
The user picks with --distro devuan|debian|ubuntu and --suite <codename>|stable, and the default lives in /etc/nextbsd-linux.conf.
GET http://deb.devuan.org/merged/dists/stable/Release gives Codename: excalibur. Plain HTTP, because deb.devuan.org currently fails TLS name verification. Signatures are still checked against the keyring.GET https://deb.debian.org/debian/dists/stable/Release gives Codename: trixie.changelogs.ubuntu.com/meta-release-lts, take the newest supported LTS, and confirm the port ships its script.Pin the codename, don’t follow stable. The wrapper writes the codename into sources.list and a state file, not stable. Writing stable would silently jump a working root to the next major release on release day. Moving to a new release should be an explicit nextbsd-linux upgrade-release, never implicit.
sysutils/debootstrap 1.0.144 is NO_ARCH, with host arch taken from sysctl hw.machine_arch (aarch64 maps to arm64). It uses gtar and ar. Its dependencies are bash, gpgv, gtar, pcre2, wget and ubuntu-keyring. The pkg-message asks for linux64 fdescfs linprocfs linsysfs tmpfs, all compiled into NextBSD’s kernel./etc/resolv.conf and hostname in.chroot $TARGET /bin/true. debootstrap then mounts devfs, fdescfs linrdlnk and linprocfs inside the target itself, runs dpkg, and unmounts on exit (functions:1232-1283, 1356-1366).dpkg --configure is retried.APT::Cache-Start 251658240 is written to apt.conf.d/00freebsd, because the Linuxulator lacks mremap(2) and apt otherwise fails with “Dynamic MMap ran out of room”.00freebsd itself.rootfs.tar.xz images for trixie on amd64 and arm64v8. Unpacking one needs no Linux code at all. It is a good fallback if stage 2 ever breaks.What ships. The nextbsd-linux command, and /usr/bin/apt as a shim to nextbsd-linux apt. Both go in NextBSD-userland, whose package build lists every staged file automatically, so no packaging change is needed. The Linux mounts themselves are not the wrapper’s job: launchctl bootstrap makes them at every boot (mounts plan §8).
| Subcommand | Behaviour |
|---|---|
apt <args> | Root only. There is no sudo on a stock image, so print “run as root” rather than exec it. It ensures a root (bootstrapping on first use), refreshes the host files, then runs chroot $root /usr/bin/apt "$@" with apt’s exit code passed through. |
bootstrap [--distro --suite --arch --mirror --variant] | Explicit bootstrap without running apt. |
run <prog> [args] | Any user. It ensures the mounts, then execs the program directly, and the Linuxulator resolves its paths through the alt root. |
shell | chroot $root /bin/bash as root. |
status | Distro and codename, arch, mounts, whether emul_path matches, and GPU notes (for example, “NVIDIA: no Linux acceleration”). |
destroy | Unmount (refuses if busy), then rm -rfx. |
| (mounts) | None: launchctl bootstrap mounts the Linux ABI filesystems at every boot (#244). The wrapper only remounts proc after debootstrap. |
What first use does:
compat.linux.emul_path. It counts as a valid root only if etc/os-release, var/lib/dpkg/status and the wrapper’s own .nextbsd-linux/state all exist. An existing directory that fails this check is refused with exit 65, and the wrapper never deletes anything on the user’s behalf. The empty pre-created mount skeleton is the one exception: bootstrapping into it is allowed.y on a tty or -y. Without a tty and without -y, exit 64 with the exact command to run. On live media, refuse unless --force: /cow is RAM-backed and unswapped (#326), and a root plus Chrome is well over 600 MB.lockf, then run debootstrap --arch=… --variant=minbase --keyring=… <codename> $root <mirror> in place. Afterwards, whatever the result, remount $root/proc (mount -t linprocfs linprocfs $root/proc), which debootstrap unmounted. On failure, leave the root for inspection and point to destroy..nextbsd-linux/state. Write 00freebsd and host.conf (order hosts, bind)./etc/resolv.conf (IPConfiguration rewrites it, so copy rather than symlink) and /etc/hosts.Users. A direct-exec program sees the root’s /etc/passwd, because it exists. linux_base-rl9 deliberately omits passwd, group and nsswitch.conf so lookups fall through to the host. The recommendation is to add host users to the root’s passwd and group at bootstrap and on run, which is what Handbook users do by hand.
Exit codes (sysexits):
apt’s own codes pass through.
linux_ioctl_drm() fixes up the direction bits and calls sys_ioctl. The dma-buf and sync_file ioctls are explicitly allowed (sys/compat/linux/linux_ioctl.c:2578-2620). So Mesa in the root drives NextBSD’s drm-kmod 6.12-lts kexts through /dev/dri, which the root’s devfs provides.xe isn’t in NextBSD’s drm-kmod, so a Mesa that prefers xe won’t help on the newest Intel GPUs./dev/nvidia* (nextbsd-kernel-extensions tools/nvidia-portpatch.sh:90-94). Linux programs get llvmpipe whatever the distro. Re-enabling means satisfying the kext’s MODULE_DEPEND(nvidia, linux) in a module-less kernel, then installing a version-matched NVIDIA userland into the root. That would be NVIDIA-Linux-*-595.84.run, installed the way x11/linux-nvidia-libs does it. No distro package matches: Debian has 550.163, Ubuntu 26.04 has 595.58, and FreeBSD ports has 595.99. That is its own project./sys/class/drm/cardN and /sys/dev/char/226:N when the DRM device’s parent is a PCI display device (sys/compat/linsysfs/linsysfs.c:377-401). vc4 and v3d are on the platform bus, so Linux drmGetDevices2() finds nothing. This is the Linux-side sibling of nextbsd#449, but here the fix belongs in linsysfs, not libdrm. PCI GPUs on amd64 are fine.$root/usr/bin on it, because ls, sh and friends would shadow the native ones. Use nextbsd-linux run <prog>, or a link subcommand that writes a stub into /usr/local/bin, the way the google-chrome and claude port wrappers do..app bundles and has no .desktop parser. A Linux app needs a generated .app wrapper whose executable is the stub. link could generate one from the package’s .desktop file./dev/fuse plus the compat mounts, and Gershwin already mounts squashfs payloads (#460).main in commit 459ac3044b (PR 289739, 2026-04-26). It is not in releng/15.0 or stable/15. Trixie’s own loader symlinks are relative, so the base root works. But update-alternatives symlinks are absolute by design: /usr/bin/x-www-browser points to /etc/alternatives/…, which points to /usr/bin/google-chrome-stable. On an unfixed kernel, direct exec through them resolves on the host. We need to check NextBSD’s 15.1-based kernel, and backport the commit if it’s missing./run needs a tmpfs (one bookworm report), Chrome’s actual /dev/shm use, bootstrap time and size.linux_base-rl9. Ports hardcode /compat/linux. If both Debian and ports’ linux-* packages must coexist, the Debian root moves to /compat/debian with emul_path set. Pick one model; mixing is messy either way.linux_base-* anyway is possible (path_glob "/compat/linux/*", which fires for files installed by other packages).| Ticket | Epic |
|---|---|
| userland#190: the Linux ABI mounts, done in launchctl bootstrap (#244) | E15 |
nextbsd-userland#241: nextbsd-linux bootstrap|apt|run|shell|status|destroy plus the /usr/bin/apt shim; vendored Devuan script; shipped keyrings | E13 |
| nextbsd-kernel#247: kernel, check for and backport 459ac3044b (alt-root absolute symlinks) | E13 |
| nextbsd-kernel#248: kernel, linsysfs DRM nodes for platform-bus devices (Pi 5 vc4/v3d) | E13 |
| nextbsd#493: spike, NVIDIA Linux acceleration (LINUX bridge plus a 595.84-matched userland) | E12 |
nextbsd-userland#242: .app wrappers for Linux apps (nextbsd-linux link) | E11 |
nextbsd-userland#243: optional, restore native WatchPaths/PathState in launchd | E2 |
Evidence: FreeBSD ports sysutils/debootstrap (Makefile, pkg-plist, pkg-message, files/patch-*), emulators/linux_base-rl9, www/linux-chrome, misc/claude-code, x11/linux-nvidia-libs; upstream debootstrap (functions, scripts/{debian-common,sid,gutsy}) and debootstrap_1.0.145devuan1; Handbook linuxemu chapter; deb.debian.org, deb.devuan.org/merged and Ubuntu Release files and Packages indexes; dl.google.com/linux/chrome/deb; FreeBSD sys/compat/linux/{linux_util,linux_ioctl}.c, sys/compat/linsysfs/linsysfs.c, sys/kern/vfs_lookup.c (main 459ac3044b); nextbsd-kernel-extensions build.yml and tools/nvidia-portpatch.sh; nextbsd-pkg scripts/build.sh. Related: E13 (#461), #460, #449, #326, E14 (#468).