A Linux distribution with a NetBSD userland, the musl C library and an LLVM toolchain. It contains no GNU software.
This document covers FreeLinX base 1.3.1: what it is, how to install and run it, how it works inside, and how to build it from source.
Contents:
FreeLinX is an independent distribution. It is not derived from Debian, Alpine or any other distribution. It combines:
| component | FreeLinX uses | instead of |
|---|---|---|
| kernel | Linux 6.18 LTS | - |
| C library | musl 1.2.5 | glibc |
| compiler and linker | clang, LLD, compiler-rt, libc++ (LLVM) | GCC, binutils ld, libgcc, libstdc++ |
| command line tools | NetBSD userland (ls, cp, sh, awk, grep, sed, tar, ...) | GNU coreutils, bash, gawk, grep, sed, tar |
| init | runit | systemd, sysvinit |
| device manager | mdevd | udev |
| interactive shell | mksh | bash |
| terminal library | netbsd-curses | ncurses |
| package manager | xpkg (written for FreeLinX) | - |
| installer | xsetup (written for FreeLinX) | - |
| boot loader | Limine 12.9 | GRUB |
Software that is GPL-licensed but not part of the GNU project, such as the Linux kernel, is allowed. What is excluded is GNU code: glibc, GCC and its runtime, and GNU libraries and tools. See section 13 for how this is checked.
FreeLinX base is a console system with a shell, network tools, an editor, a C compiler and the package manager. Everything else is installed from the package repository. This document is about base.
FreeLinX desktop (repository FreeLinX-desk) is built from the same packages and boots into an Openbox desktop with a browser based on Firefox ESR. Base can get close to it by installing packages (see section 12).
| area | software |
|---|---|
| system | runit, mdevd, dbus, doas, toybox (getty, login, ps, top, pgrep, free, uptime) |
| shells | mksh R59 (login shell), NetBSD sh (/bin/sh, for scripts) |
| network | dhcpcd, wpa_supplicant, flxwifi, iproute2 (ip), OpenSSH 10.5 (ssh, sshd, scp, sftp), curl, ntpd |
| files and disks | e2fsprogs, dosfstools, flxpart, lsof |
| editors and tools | vim (also vi), less, tmux, htop, nnn, git, fastfetch |
| documentation | mandoc (man, apropos, whatis), about 200 NetBSD manual pages |
| development | tcc (as cc) with the musl and Linux headers |
| firmware | linux-firmware: Intel, AMD, Atheros, Realtek, MediaTek, Broadcom and others |
X11, GTK, Mesa and fonts are not on the image.
This section is an overview of the whole system: its layers, what happens from power-on to the login prompt, which processes run, where files are and how programs find their libraries. The later sections describe each part in detail.
+-------------------------------------------------------------+ | your programs: st, firefox, vim, git ... (from xpkg) | +-------------------------------------------------------------+ | services: runit, mdevd, dbus, dhcpcd, ntpd, sshd, flxwifi | | tools: NetBSD userland, toybox, mksh, xpkg, xsetup | +-------------------------------------------------------------+ | libraries: libc++, openssl, sqlite, zlib, netbsd-curses ... | +-------------------------------------------------------------+ | musl: the C library and the dynamic loader | +-------------------------------------------------------------+ | Linux kernel: drivers, filesystems, network, processes | +-------------------------------------------------------------+ | firmware (BIOS or UEFI) and Limine | +-------------------------------------------------------------+
Every program above the kernel was compiled with clang against musl. Each layer uses only the layers below it. There is no compatibility layer for glibc: a program built for another distribution's glibc does not run on FreeLinX and has to be rebuilt.
limine.conf, shows the menu, loads the
kernel (and, on the live medium, the small initramfs) and passes the kernel
command line.On a virtual machine with KVM this takes a few seconds.
PID 1 runsvdir -P /var/service
runsv shell -- flxconsole
getty ttyS0 / tty1 / tty2 / tty3
login -- mksh (after you log in)
runsv dbus -- dbus-daemon --system
runsv ntpd -- ntpd
runsv flxwifi
runsv sshd -- sshd (if enabled)
mdevd (started by /init)
dhcpcd (started by /init)
That is the whole system: about twenty processes and 55 MB of RAM. If a supervised service dies, runsv starts it again. If a login session ends, flxconsole starts a new getty on that console.
| path | contents |
|---|---|
/init | the boot script, process 1 until it starts runsvdir |
/bin, /sbin | essential commands: the NetBSD tools, sh, mksh, runit, mdevd, e2fsprogs, the flx* system scripts |
/lib | the musl library and loader (ld-musl-x86_64.so.1), kernel modules in /lib/modules, firmware in /lib/firmware |
/usr/bin, /usr/lib | everything else: programs and shared libraries from packages |
/usr/include | C headers for musl and Linux (used by cc) |
/usr/share | manual pages, time zones, fonts, terminal descriptions, data |
/etc | configuration, all plain text |
/etc/svc | available but disabled services |
/var/service | enabled services (directories or links into /etc/svc) |
/var/lib/xpkg | the package database |
/var/cache/xpkg | downloaded package archives |
/var/log | log files: mdevd.log, ntpd.log, sshd.log and others |
/run | tmpfs: sockets and pid files, empty at every boot |
/tmp | tmpfs: temporary files, empty at every boot |
/home | user directories, on their own partition |
/root | root's home directory |
/media/flx | the live medium, read-only, when one is attached |
/boot | a copy of the kernel; the boot loader reads the one on the ESP |
Every dynamically linked program names /lib/ld-musl-x86_64.so.1
as its interpreter. When the kernel starts the program it first loads that
file, which is musl itself: in musl the C library and the dynamic loader are
the same file. The loader then finds the other libraries the program needs in
the directories listed in /etc/ld-musl-x86_64.path:
/lib /usr/local/lib /usr/lib
There is no ldconfig and no library cache: a library is found as soon as
it is in one of these directories. ldd PROGRAM shows what a
program loads.
Everything is configured with text files in /etc, edited with any editor.
There is no registry, no configuration daemon and no binary log. Changes take
effect when the program reads the file again: restart the service with
sv restart NAME, or log in again for /etc/profile. xsetup only
writes these files; anything it does can also be done by hand.
| file | what it sets |
|---|---|
| /etc/hostname, /etc/hosts | host name |
| /etc/passwd, /etc/group, /etc/shadow | users, groups, password hashes |
| /etc/doas.conf | who may run commands as root |
| /etc/profile | environment for login shells: PATH, TZ, prompt, SVDIR |
| /etc/dhcpcd.conf, /etc/resolv.conf | network addresses and name servers |
| /etc/wpa_supplicant/flxwifi.conf | saved Wi-Fi networks |
| /etc/mdev.conf | device permissions and hotplug actions |
| /etc/fstab, /etc/flx-disk | filesystems and the partitions of this installation |
| /etc/localtime, /etc/TZ | time zone |
| /etc/xpkg/ | package repositories and trusted keys |
| /etc/ssh/ | SSH client and server |
| /etc/os-release | the release name and version |
The ISO contains the base system. Everything else comes from the package
repository through xpkg: X, window managers, browsers, media players,
compilers and libraries. The base system itself is also made of packages from
the same repository, plus the files in the src repository (the boot script,
configuration and the flx* scripts). An installed system can be brought up to
date in two ways: xpkg upgrade for packages, and
flxupgrade from a newer ISO for the base files.
Releases are on GitHub. Each release has the ISO image and a sha256 file.
curl -LO https://github.com/FreeLinX/FreeLinX-base/releases/latest/download/freelinx-base-x86_64.iso curl -LO https://github.com/FreeLinX/FreeLinX-base/releases/latest/download/freelinx-base-x86_64.iso.sha256 sha256sum -c freelinx-base-x86_64.iso.sha256
The ISO is a hybrid image: it can be written to a USB stick as it is.
lsblk # find the stick, for example /dev/sdb dd if=freelinx-base-x86_64.iso of=/dev/sdX bs=4M conv=fsync
Check the device name carefully: dd overwrites the whole device. Then boot the computer from the stick. Both legacy BIOS and UEFI boot are supported.
Create a disk image once, then boot the ISO with the disk attached:
qemu-img create -f qcow2 flx.qcow2 20G qemu-system-x86_64 -enable-kvm -cpu host -m 4096 -smp 2 \ -drive file=flx.qcow2,if=virtio,format=qcow2 \ -cdrom freelinx-base-x86_64.iso -boot d \ -device VGA,xres=1600,yres=900 \ -nic user,model=virtio-net-pci
After installation, start the same command without -cdrom and
-boot d to boot from the disk. Running qemu-img create
again empties the disk.
-device VGA,xres=...,yres=... tells the guest the preferred
screen size; both the text console and X use it. With plain
-vga std the guest gets a small default mode.
For UEFI, add OVMF:
cp /usr/share/OVMF/OVMF_VARS_4M.fd vars.fd qemu-system-x86_64 -enable-kvm -cpu host -m 4096 -smp 2 -machine q35 \ -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \ -drive if=pflash,format=raw,file=vars.fd \ -drive file=flx.qcow2,if=virtio,format=qcow2 \ -cdrom freelinx-base-x86_64.iso -boot d \ -device VGA,xres=1600,yres=900 -nic user,model=virtio-net-pci
Without -enable-kvm QEMU emulates the CPU and everything is
10 to 20 times slower.
When the ISO boots, Limine shows a menu with two entries, "FreeLinX 1.3.1 base (installer: xsetup)" and "Rescue shell". The first one starts after five seconds. The kernel messages are shown, then the screen is cleared and the banner appears with a root prompt.
The live system gives a root shell without a password on tty1, tty2, tty3
(switch with Alt+F1 to Alt+F3) and on the serial port ttyS0. There is also a
user live, which may use doas without a password; it is removed
during installation.
The system files are read from the medium. Changes are stored in RAM and are lost on reboot. The writable layer can use up to 75% of RAM. A large package like Firefox does not fit in a live session on a small machine; install to disk first.
The medium itself is mounted read-only at /media/flx.
Network: on wired interfaces dhcpcd starts at boot. Wi-Fi is configured
with flxwifi (see section 10).
Run xsetup as root. It runs thirteen steps in order, each one
asking its questions and writing its files. A finished step is recorded in
/etc/xsetup.state; if the installer stops, running
xsetup again continues with the first unfinished step.
xsetup # run all steps that are not done xsetup --list # list the steps xsetup --status # show which steps are done xsetup --reset STEP # mark a step as not done xsetup setup-hostname # run one step
/etc/conf.d/loadkmap.conf. It is not applied to the running text
console: current Linux kernels no longer allow programs to change the console
keymap. X uses its own layout setting./etc/hostname and
/etc/hosts. Names follow RFC 1123: letters, digits, dots and
dashes, labels of 1 to 63 characters, at most 253 in total, no label starting
or ending with a dash, no leading or trailing dot./etc/dhcpcd.conf. If there is a wireless interface it offers to
set up Wi-Fi and saves the network in
/etc/wpa_supplicant/flxwifi.conf (mode 0600); the key is stored
as the 64-digit PSK computed by wpa_passphrase, and for WPA3 also as
sae_password when the passphrase can be quoted./etc/shadow by flxpasswd, which uses the C library's
crypt(3)./usr/share/zoneinfo, copies
the zone to /etc/localtime and writes the name to
/etc/timezone and /etc/TZ. /etc/profile
exports TZ from /etc/TZ./etc/proxy.conf (mode 0600) and
/etc/profile.d/proxy.sh, which exports http_proxy and https_proxy.
The proxy password is read without echo./etc/svc/ntpd into
/var/service. Correct time matters: TLS certificate checks fail
with a wrong clock./etc/xpkg/repos.conf and
runs xpkg update. The default is the official signed repository.
A plain http:// URL is accepted only after a warning./home/NAME with mode 0700, and the shell is the one named in
/etc/flx-shell (mksh)./etc/svc/openssh to
/var/service/sshd.yes. It then partitions, formats, copies the system and installs
the boot loader. Details are in the next section.flxpart --create-standard: ESP, BIOS boot,
root and home partitions./etc/motd and /etc/issue
so it no longer says "Live system"./etc/flx-installed, writes /etc/flx-disk and
/etc/fstab.EFI/BOOT/BOOTX64.EFI) and BIOS (limine bios-install
into the BIOS boot partition), and writes limine.conf with the
root partition's PARTUUID.When xsetup finishes, power off, remove the medium and boot from the disk.
| # | partition | filesystem, label | size | mounted at |
|---|---|---|---|---|
| 1 | EFI system | FAT32, FLX_BOOT | 1 GiB | not mounted |
| 2 | BIOS boot | none | 1 MiB | - |
| 3 | root | ext4, FLX_ROOT | see below | / |
| 4 | home | ext4, FLX_HOME | the rest of the disk | /home |
Root partition size: on disks of 20 GiB or more, 30% of the disk, at least 6 GiB and at most 64 GiB; on smaller disks 40%, at least 3 GiB. The minimum disk size is 8 GiB.
Names the two ext4 filesystems by UUID. /init mounts /home from
this pin, so another disk with a partition labelled FLX_HOME is never mounted
by mistake.
FLX_ROOT_UUID=2db63e9e-... FLX_HOME_UUID=792ae350-...
UUID=2db63e9e-... / ext4 defaults,noatime 0 1 UUID=792ae350-... /home ext4 defaults,noatime 0 2 tmpfs /tmp tmpfs mode=1777,nosuid,nodev 0 0
The root filesystem is mounted by the kernel and /home and /tmp by
/init; fstab documents them.
On the ESP (also copied to boot/ and EFI/BOOT/):
timeout: 3
serial: yes
textmode: yes
interface_branding: FreeLinX 1.3.1
/FreeLinX 1.3.1
protocol: linux
kernel_path: boot():/boot/bzImage
cmdline: root=PARTUUID=... rootfstype=ext4 rootwait ro init=/init console=ttyS0,115200 console=tty0
/Rescue shell
protocol: linux
kernel_path: boot():/boot/bzImage
cmdline: root=PARTUUID=... rootfstype=ext4 rootwait ro init=/init console=ttyS0,115200 console=tty0 flx.rescue=1
The PARTUUID is the GPT partition GUID of the root partition. setup-disk
reads it from the partition table directly, because the system's blkid does
not report it. console=tty0 is last, so /dev/console
is the screen; kernel messages also go to the serial port.
Limine --> kernel + initramfs --> flxlive --> overlay root --> /init --> runsvdir
The ISO contains:
boot/bzImage | the kernel |
boot/initramfs.img.gz | about 290 KB: flxlive as /init and a static /bin/sh |
boot/root.sfs | the system, squashfs compressed with zstd, about 290 MB |
boot/limine/, EFI/BOOT/ | Limine for BIOS and UEFI |
flxlive is a 46 KB static C program (scripts/flxlive.c). It is written in C because switching to a new root needs MS_MOVE mounts, chroot and a loop device, and the userland has no switch_root, chroot or losetup commands. It does the following:
FREELINX_LIVE (the label can be changed with
flx.medium=LABEL on the kernel command line), waiting up to 30
seconds for slow USB and CD drives;boot/root.sfs to a free loop device, read-only;If any step fails, flxlive prints which one and why and starts /bin/sh.
Limine --> kernel mounts FLX_ROOT read-only --> /init --> runsvdir
There is no initramfs. The kernel has the disk drivers (virtio, NVMe, AHCI,
SCSI disk, USB storage and UAS), the EFI and MBR partition parsers and ext4
built in, so it can find the root partition by PARTUUID and mount it by
itself. rootwait makes it wait for slow devices.
/init is a shell script and runs as process 1, both on the live
medium and on an installed system. In order it:
e2fsck -p on it. Exit code 1 means errors were fixed; 2 or 3
means the root filesystem was changed and the machine reboots; 4 or more
means errors that need manual repair, and a shell is started. Then it remounts
/ read-write and mounts a tmpfs on /tmp;flx.rescue=1, starts a
root shell on the console instead of the services;runsvdir -P /var/service.poweroff and reboot run flx-shutdown. It stops
every service with sv down, sends TERM and then KILL to all
processes, syncs, unmounts every disk filesystem in reverse order (or remounts
it read-only if it is busy), remounts / read-only, and finally triggers the
kernel's emergency sync, unmount and power-off or reboot through
/proc/sysrq-trigger.
Each directory in /var/service is a service supervised by
runsv. A service directory contains an executable run script that
starts the program in the foreground.
| service | what it runs |
|---|---|
| shell | flxconsole: shells or logins on the consoles |
| dbus | the system message bus, on /run/dbus/system_bus_socket |
| ntpd | time synchronisation, logs to /var/log/ntpd.log (link to /etc/svc/ntpd) |
| flxwifi | connects to saved Wi-Fi networks at boot |
| sshd | OpenSSH server, if enabled (link to /etc/svc/openssh), logs to /var/log/sshd.log |
sv status /var/service/* # state of all services sv restart sshd # restart one (SVDIR=/var/service is set in /etc/profile) sv down ntpd # stop until the next boot ln -s /etc/svc/openssh /var/service/sshd # enable rm /var/service/sshd # disable
The kernel creates device nodes in devtmpfs. mdevd receives kernel events
and applies the rules in /etc/mdev.conf: it sets owners and modes,
loads modules and calls helper scripts. Some rules:
null 0:0 0666 zero 0:0 0666 random 0:0 0666 urandom 0:0 0666 tty 0:5 0666 console 0:5 0600 sd[a-z][0-9]* 0:6 0660 */sbin/flxautomount nvme[0-9]*n[0-9]*p[0-9]* 0:6 0660 */sbin/flxautomount sr[0-9]* 0:6 0660 */sbin/flxautomount
flxautomount mounts removable media when they appear. It does nothing while the installer is partitioning a disk.
The shell service runs /sbin/flxconsole, which looks after
/dev/ttyS0, /dev/tty1, /dev/tty2 and /dev/tty3:
TERM is linux on the screen and vt100 on a serial
line. The prompt shows user, host and the current directory.
Members of the group wheel can run commands as root with doas, after
entering their own password (remembered for a while with
persist). /etc/doas.conf:
permit persist :wheel permit nopass :wheel cmd /sbin/reboot permit nopass :wheel cmd /sbin/poweroff permit nopass :wheel cmd /usr/bin/zzz
There is no sudo.
passwd changes your own password (through doas) or, as root,
anyone's. It asks twice with echo off and stores the hash with
flxpasswd.flxadduser NAME adds a user with the groups video, audio,
storage, input and users and asks for a password; --admin also
adds wheel./etc/flx-shell names the interactive shell
(/bin/mksh). /bin/sh remains the NetBSD sh for scripts.A forgotten root password can be reset from the "Rescue shell" boot entry:
it gives a root shell with the disk mounted read-write; run
passwd root.
dhcpcd runs from /init on eth0. Static settings made with setup-interfaces
are kept in a block in /etc/dhcpcd.conf:
# --- xsetup: begin --- interface eth0 static ip_address=192.168.1.10/24 static routers=192.168.1.1 static domain_name_servers=192.168.1.1 # --- xsetup: end ---
dhcpcd writes /etc/resolv.conf. ip addr,
ip route and ifconfig show the state.
flxwifi scan # list networks FLXWIFI_PASS='passphrase' flxwifi connect "MyNet" flxwifi status flxwifi list # saved networks flxwifi disconnect
flxwifi runs wpa_supplicant (nl80211) and dhcpcd. Passing the passphrase in
FLXWIFI_PASS keeps it out of the process list. Saved networks are in
/etc/wpa_supplicant/flxwifi.conf and are joined at boot by the
flxwifi service. The firmware for common Intel, Atheros, Realtek and MediaTek
cards is included, and the regulatory database is installed.
flxnet is a small text menu for wired and wireless setup.
Enable the server with xsetup setup-sshd or by linking
/etc/svc/openssh into /var/service. Configuration is in
/etc/ssh/sshd_config; host keys are created on first start.
xpkg update # download the repository index xpkg search WORD # search names and descriptions xpkg show NAME # details of a package in the repository xpkg install NAME... # install with dependencies xpkg install ./file.xpkg # install a local archive xpkg remove NAME... xpkg autoremove # remove dependencies no longer needed xpkg upgrade # update the index and upgrade everything xpkg outdated # list packages with newer versions xpkg list -e # packages you installed explicitly xpkg info NAME # details of an installed package xpkg files NAME # files of an installed package xpkg owns /usr/bin/st # which package owns a file xpkg verify # check installed files against the database xpkg clean # delete downloaded archives
Common options: -n shows what would happen without doing it,
-q and -v change the output, --root DIR
operates on a system mounted at DIR.
/etc/xpkg/repos.conf | repository URLs, one per line, highest priority first |
/etc/xpkg/keys/ | trusted public keys (Ed25519) |
/var/lib/xpkg/xpkg.db | installed packages and their files (SQLite) |
/var/cache/xpkg/ | downloaded indexes and archives |
A .xpkg file is a gzip-compressed ustar archive:
pkg-info NAME, VERSION, DESCRIPTION, ARCH, DEPENDS (key=value lines) post-install optional sh script, run after install and upgrade pre-remove optional sh script, run before removal files/... the files, relative to /
Example pkg-info:
NAME=xinit VERSION=1.4.4-3 DESCRIPTION=xinit, startx and the default X session ARCH=x86_64 DEPENDS=libX11,musl
xpkg reads the archive itself; it does not call tar, gzip or curl.
A repository is a directory served over HTTPS with index.json,
index.json.sig and the archives. For each package the index lists
version, description, dependencies, file name, size and sha256.
index.json.sig is an Ed25519 signature of the exact index
bytes. xpkg checks it against the keys in /etc/xpkg/keys and refuses the
repository if it does not verify. With no key installed it refuses every
repository.The official repository is the Hugging Face dataset FreeLinX/packages with 428 packages. Every package passes check-nognu before it is published.
Files under /etc are treated as configuration. If a package update brings a
new version of a file you have changed, your file is kept and the new one is
written next to it as FILE.xpkgnew.
xpkg install xorg xinit openbox startx
The xorg package depends on the X server, the libinput and
evdev input drivers, the fbdev video driver, Mesa, xkbcomp and the XKB layout
data. openbox depends on fonts (DejaVu) and the st terminal. The
modesetting video driver built into the server is used on most hardware and
in QEMU.
Without ~/.xinitrc, startx runs
/etc/X11/xinit/xinitrc. It picks the first installed terminal
(st, urxvt or xterm) and window manager (openbox, dwm or twm). With openbox it
runs exec openbox --startup st, so the terminal starts after
openbox has taken over the screen. A terminal started at the same moment as
the window manager can be lost and never appear.
Your own ~/.xinitrc should therefore end like this:
exec openbox --startup st
Right click on the desktop opens the root menu: Terminal, Reconfigure,
Restart, Exit. The defaults are in /etc/xdg/openbox/rc.xml and
/etc/xdg/openbox/menu.xml; copy them to
~/.config/openbox/ to change them.
xpkg install xrandr xrandr # list modes xrandr -s 1920x1080
To make it permanent, put the xrandr line in ~/.xinitrc before the exec line.
Examples from the repository: firefox, mpv, feh, nsxiv, mupdf, dillo, xfe, tint2, dwm, dmenu, urxvt, ffmpeg. Firefox needs about 500 MB of space. The first start of st or another program that uses fontconfig takes a few seconds while the font cache is built.
Every image and every package is checked by
check-nognu.sh before release. It examines every ELF file and
fails if any of these is true:
/lib/ld-musl-x86_64.so.1;The last check exists because GNU code compiled with clang and linked statically leaves no GCC marker and no library dependency.
doas xpkg upgrade
This updates everything installed from the repository, including the kernel package.
Boot the ISO of the new release on the installed machine and run
flxupgrade as root. It:
Users, passwords, settings, packages and /home are kept. Afterwards run
doas xpkg upgrade.
Releases 1.0.13 to 1.1.x installed a different layout: the system ran from an image in RAM and kept /usr, /etc, /var and the rest on a partition labelled FLX_SYS. flxupgrade converts such a disk: FLX_SYS becomes the root partition (it is relabelled FLX_ROOT and gets the missing top-level directories), and the RAM image is removed from the ESP.
The ISO can be built on any x86_64 Linux system without root.
apt install git curl python3 openssl xorriso squashfs-tools cpio xz-utils binutils apt install qemu-system-x86 qemu-utils ovmf # only for the QEMU tests
Clone the repositories next to each other and unpack the toolchain release:
mkdir FreeLinX && cd FreeLinX
for r in FreeLinX-base src ports drivers; do
git clone https://github.com/FreeLinX/$r.git
done
curl -LO https://github.com/FreeLinX/toolchain/releases/download/v1.0.0/toolchain.tar.gz
tar -xzf toolchain.tar.gz
FreeLinX/ FreeLinX-base/ build scripts, installer, flxlive, tests src/ root filesystem ports/ console ports (built packages in ports/packages) drivers/ Limine binaries (bootloader/limine-binary) toolchain/ clang, LLD, musl sysroot
cd FreeLinX-base sh build-base.sh
Output: out/freelinx-base-x86_64.iso and its sha256 file. The
build takes a few minutes and downloads about 150 MB.
scripts/host-xpkg.sh
downloads xpkg and its libraries from the repository, verifies the index
signature (with openssl or python3-cryptography) and the archive checksums, and
runs xpkg through the downloaded musl loader.scripts/mkrootfs.sh to make the root filesystem
(below)./usr/lib/linux/bzImage-*), so kernel and modules match. Exactly
one kernel version may be present in /lib/modules.boot/root.sfs with mksquashfs (zstd,
1 MiB blocks, all files owned by root).scripts/flxlive.c statically with the musl
toolchain, checks it with check-nognu, and packs it with a static sh as the
initramfs.limine bios-install on it so it also boots from a USB stick.src/rootfs. Refuses to continue if that tree has
uncommitted changes, unless ALLOW_DIRTY=1.ports/packages, builds the man page index, compiles the
file magic database from file-5.46 (downloaded from the FreeLinX
source mirror and checked if not present locally), and adds the repository key
and the xsetup installer.SERIAL=1 | kernel console also on ttyS0; needed by the QEMU tests |
OUT=file.iso | output path |
ALLOW_DIRTY=1 | build from a src tree with uncommitted changes |
XPKG | command that runs a host xpkg |
SYSROOT | musl sysroot (default ../toolchain/x86_64-linux-musl) |
LIVECC | C compiler command for flxlive (default ../toolchain/bin/clang with the musl target) |
REPO | package repository URL |
LIMINE_DIR | Limine files (default ../drivers/bootloader/limine-binary) |
FLXSRC | src checkout (default ../src) |
FLX_HW_TARBALL | extra firmware tarball |
BASE_FROM_DESKTOP=1 | build from a built FreeLinX-desk tree instead of src |
The libraries and programs in the repository are built by the desktop
stack (FreeLinX-desk/stack/build-stack.sh and
package-stack.sh) and by the ports tree, with the same clang and
musl toolchain, then signed and published with
xpkg/tools/publish-repo.sh, which runs check-nognu on every
archive first.
| suite | checks |
|---|---|
test-ui.sh | the installer's menus, prompts, password handling and input rules (host names, user names) |
test-setup-disk.sh | setup-disk's plan: partition sizes, minimum disk size, refusal of mounted disks, dry runs that write nothing |
test-destructive.sh | partitioning and formatting real image files with the system's own flxpart, mkfs and blkid; PARTUUID reading; UUID pins; banner rewrite |
test-banner.sh | the banner text and logo |
test-xsetup-qemu.sh [--uefi] [iso] | boots the ISO in QEMU, checks the live system (overlay root, RAM, fastfetch), runs all 13 steps, boots the installed disk, logs in as user and root, checks root filesystem, PARTUUID, /tmp, /home, services, device modes, banner, then reboots and checks that files written in /home and /etc are still there |
test-upgrade-qemu.sh old.iso [new.iso] | installs an older release, writes files, upgrades with flxupgrade, boots the upgraded disk and checks version, files, passwords, host name, services and that it runs from its root partition |
src: test-console.sh, test-flxconsole.sh, test-mdevconf.sh | the console service, login on installed systems, the mdev rules |
The QEMU tests need an ISO built with SERIAL=1; they drive the
guest over its serial port. The suites that need no virtual machine,
shellcheck and check-nognu run in GitHub Actions on every push. The QEMU suites
are run on BIOS and UEFI before each release, on the exact ISO that is
published.
Commands specific to FreeLinX. Standard commands have manual pages.
auto joins saved networks and is run at
boot by the flxwifi service.qemu-img create again on an
installed disk; it empties it.xpkg clean to free
the downloaded archives, give the machine more RAM, or install to disk
first.st & and exec openbox in
~/.xinitrc, replace them with exec openbox --startup st, or remove
~/.xinitrc to use the default session. Right click opens the menu.xpkg install fonts (current
openbox packages depend on it).xpkg install xkeyboard-config (current
xorg packages depend on it).-device VGA,xres=1600,yres=900. In X use
xrandr./etc/xpkg/repos.conf. Do not use
--allow-unsigned with untrusted repositories.date). Enable ntpd with
xsetup setup-ntp.passwd root.e2fsck /dev/... on the device named in the message, then
reboot.| FreeLinX-base | build scripts, xsetup, flxlive, tests, base releases |
| src | root filesystem: /init, system scripts, NetBSD tools, configuration, manual pages |
| ports | console ports and their built packages |
| FreeLinX-desk | desktop edition; build of the shared package stack (Xorg, GTK, Mesa, Firefox, ...) |
| xpkg | package manager, repository index and signing tools |
| toolchain | clang, LLD, compiler-rt and the musl sysroot |
| drivers | Limine binaries and installer helpers |
| kernel | kernel configuration and build |
| FreeLinX | desktop releases and this documentation |
| FreeLinX/packages | the signed package repository |
| FreeLinX/sources | mirror of upstream source archives with checksums |
FreeLinX is developed by volunteers. Donations pay for build machines, test hardware and hosting.
| Buy Me a Coffee | buymeacoffee.com/freelinxfoundation |
| Bitcoin | bc1qkp9vt9mv54gv8ze694uhrmlhxnkv9u7jgtu3w3 |
| Ethereum | 0x7c4c547aadc3d67d74a6fb602664fa2bc473ed58 |
Bug reports and patches are just as welcome: github.com/FreeLinX/FreeLinX/issues.
FreeLinX base 1.3.1 documentation, October 2026 · BSD-2-Clause · page source