Linux From Scratch (LFS) virtual machine (VM)

LFS screenshot

Figure 1: Screenshot of my LFS VM's GNOME session as of 12 July 2026.

I first installed LFS 12.4 systemd edition to a virtual machine on 9 February 2026. Since then, I have upgraded the system to the development systemd branch, and kept the system up to date. It has been a challenging, yet informative journey.

Package management

From my NixOS host machine, I have written — with the help of artificial intelligence (AI) — several shell functions that are imported into my LFS VM and provide basic package management functionality. These functions are part of both my host's and VM's shell profile. These functions can be found in my NixOS configuration user shell profile.

Table 1: Shell functions used for package management within the LFS VM.
Function name Defined in Syntax Description
Guest Host
autobuild lfs_autobuild lfs-autobuild.sh autobuild PACKAGE(S) [OPTION(S)] Default: build and install the specified package(s), if and only if the latest version of the package is not already installed. LFS/Beyond LFS (BLFS) instructions are used to build most packages. Although, some packages are built using custom build scripts defined in ~/lfs_packaging.
--dry-run: show what actions would be executed to build and install the package.
--strip: run stripping commands after build.
--no-upstream: disable upstream version searching.
--include-config: include configuration commands in the LFS/BLFS book entry.
--rm-libs: remove old library versions after build (disabled by default).
--lfs: search only in the LFS book.
--blfs: search only in the BLFS book.
--lfs-book : specify LFS book (e.g., development, systemd, stable, or full URL).
--blfs-book : specify BLFS book (e.g., systemd, development, stable, or full URL).
--skip-tests: skip test commands (make check/test, etc.).
--ignore-test-failures: ignore test failures by appending '|| true' to test commands.
-f/--force: force rebuild and installation even when latest version is already installed.
-h/--help: show help message.
autoremove lfs_autoremove 21-lfs.sh autoremove PACKAGE(S) [OPTION(S)] Default: remove the specified package(s), if and only if no other packages have libraries that depend on the package(s).
--dry-run: show what actions would be executed to remove the package.
-f/--force: force removal, without regard for library dependencies.
N/A cleanup_old_libraries_gpt 21-lfs.sh cleanup_old_libraries_gpt Remove old unused libraries. As for used old used libraries, rebuild packages that depend on the library and then remove it.
N/A cleanup_old_doc_dirs_gpt 21-lfs.sh cleanup_old_doc_dirs_gpt Remove old unused documentation directories.
update lfs_update 21-lfs.sh update [OPTION(s)] Update packages. --dry-run: Show what would be updated without downloading/building.
--no-upstream: Check only LFS/BLFS book versions (disable upstream tracking).
-h/--help: Show help message.
updates lfs_updates lfs-updates.sh updates Print table that list available updates (marked with [UPDATE]), as well as packages with missing inventories (marked with [FILES MISSING]) and packages with versioning failures (marked with [FAILED]).

Package inventories are stored within /var/lib/book-packages for LFS/BLFS packages and /var/lib/custom-packages for custom packages.

Custom desktop configuration files

These are defined in ~/lfs_apps. They include desktop configuration files that generate plots of boot times and cycle through wallpapers.

Custom packages

~/lfs_packaging provides custom build scripts to install many packages. Some of these packages are also provided by LFS and BFLS — such as Linux PAM, Vim, rustc and packages within the xorg-apps and xorg-libs metapackages. I provide these custom packages sometimes to overcome build errors that the book-extraction function cause and other times to more robustly ensure I have the latest version of these packages at all times. Other packages are provided in this repository because LFS and BLFS do not provide them; other books in the LFS such as SLFS do provide some of these packages, but some are unique to this repository (e.g. GNU Octave and R are).

Fastfetch/HyFetch

I have also customized Fastfetch/HyFetch output so that it accurately prints the number of packages I have installed. The configuration file used is located in ~/lfs-scripts/config.jsonc. In the screenshot above, 807 (󰌽 573,  130,  1,  74,  29) means that 807 packages are installed in total. Of them 573 are LFS or BLFS book packages installed via autobuild and its extracting build commands and source URLs from the books' webpages. A further 130 were installed via custom build scripts in ~/lfs_packaging. 1 Julia package was installed; this package is Julia itself which was installed via juliaup (the compilation process of Julia is incredibly complex and even requires its own custom build of LLVM). 74 Python packages were installed via pip. 29 R packages were installed.

Shell profile

My shell profile is defined in ~/lfs-scripts. Some scripts called for by GNOME and KDE Plasma Executor/Command Output commands are in this repository, too.

GNOME

GNOME was the first desktop I installed. Its packages are kept at the latest upstream version, not merely the latest version in the BLFS book. Dash to Dock is enabled and installed, as is WeatherPanel and Show Desktop Button. Executor is another extension I use; I've actually created my own fork with more features.

Executor fork

Installed via the executor ~/lfs_packaging package. It can be installed via more standard ways, too.

The base executor extension provides up to three widgets in the GNOME panel on the left, centre and right of the panel. In these widgets is displayed the output of specified commands. The interval at which the command is re-run can also be specified. The executor fork I maintain provides the following additional features:

  • Tooltips, which can include command output at their end as well as the static text within the widget that precedes the command output; and

  • Command execution upon clicking the widget in the panel. Left, middle and right click options are specifiable.

Scripts called by the extension

Includes:

  • count-wallpapers.sh — displays the number of the currently shown wallpaper / the total number of wallpapers in ~/wallpapers. In my set up, it is used to generate output by the centre widget.

  • list-wallpaper.sh — displays the list of wallpapers in ~/wallpapers with the currently shown wallpaper highlighted and centred. gnome-terminal -- zsh -ic "~/lfs-scripts/list-wallpapers.sh" is run when left-clicking the centre widget in my setup.

  • open-wallpaper.sh — opens the displayed wallpaper in Eye of GNOME. This script is run when I right-click the centre widget in my setup.

  • updates_no.sh — checks for updates using the updates command in the shell profile. It displays $in_progress $mod_time  $no_updates 󰂕 $no_missing_total  $no_failed where $in_progress is replaced with nothing if the updates command is not running, and 󰦕 ${percent}% otherwise, where $percent is an approximation of how far through the running of updates we are. $mod_time is replaced with the time the updates command last stopped running. $no_updates is replaced with the number of available package updates. $no_missing_total is replaced with the number of packages with missing inventories. $no_failed is replaced with the number of package versioning failures.

  • update-table.sh — generates a more compact table of packages with updates, missing inventories and versioning failures. It is run to generate the tooltip for the right widget.

Left-clicking the right widget opens a terminal in which update is run. Middle-clicking the right widget re-runs updates to update the displayed values. Right-clicking the right widget runs tail -f ~/updates.log (~/updates.log contains the output of the last updates run).

KDE Plasma

KDE Plasma was the second desktop I installed. Panel Spacer Extended extension is installed, as is the Command Output Plasma widget.