// forge linux — your distro from source · 3 aug 2026

Build Forge Linux

From bare-metal toolchain to a named distribution you own — branded identity, package path, live ISO, installer, and release engineering. Host Fedora 44 · LFS 13.0-aligned packages · kernel 7.1.5.

Kernel
7.1.5 (stable)
GCC
15.2.0
glibc
2.43
Binutils
2.46.0
systemd
259.1
Host
Fedora 44
Chapter 00

Introduction

This guide is a spiritual successor to the Build Your Own Linux course originally offered by Linux Academy, updated for 2026 toolchains and modern system design. Like the original, it uses Fedora as the host system and walks you through building every component of a working Linux distribution from source code.

The approach is fundamentally the same as the venerable Linux From Scratch project — you compile everything yourself, understand every dependency, and end up with a minimal, bootable system. What this guide adds is Fedora-centric tooling context, package versions aligned with LFS 13.0-systemd (plus current kernel.org stable 7.1.5), and systemd as the init system from day one. Last refreshed 3 August 2026.

What You Will Build

By the end of this guide you will have Forge Linux — a named distro, not only a generic rootfs:

Philosophy

Chapters 00–19 build a correct minimal system (the LFS spine). Chapters 20–28 turn it into your distribution. You start with a learning system — one that gives you deep, hands-on understanding of how the pieces of Linux fit together. After completing this guide you will understand:

F
Forge Linux
VERSION 0.1.0 · CODENAME “anvil” · YOUR DISTRO

Default brand used in Chapters 20–28. Rename via the identity kit — one file drives os-release, GRUB, MOTD, hostname, and ISO labels.

Primary
#4ade80
Ink
#0d0f12
Paper
#e8edf5
Accent
#22d3ee
Warn
#fbbf24
Tip Run this entire build inside a virtual machine. VirtualBox or GNOME Boxes on Fedora both work excellently. Snapshots let you roll back if a build step goes wrong without losing hours of work.

Time Estimate

On a modern machine with 4+ cores and an SSD, expect the full build to take 4–8 hours. The GCC and glibc builds dominate the compile time. Use make -j$(nproc) throughout to parallelise.

Conventions Used

Commands are shown in code blocks. The prompt tells you who should run the command:

root# command run as root
lfs$  command run as the lfs build user
# this is a comment / explanatory note

Variables you must substitute with your own values are written $LIKE_THIS.

Chapter 01

Package Versions Reference

Always verify you are downloading the exact versions listed below. Using a different minor version can cause subtle build failures downstream, especially in the toolchain stages.

Toolchain

PackageVersionDownloadRole
Binutils2.46.0sourceware.org/binutilsLinker, assembler, object tools
GCC15.2.0gnu.org/gccC/C++ compiler collection
Linux (headers)7.1.5kernel.orgKernel API headers for glibc
Glibc2.43gnu.org/glibcGNU C library
GMP6.3.0gmplib.orgGCC dependency: arbitrary precision math
MPFR4.2.2mpfr.orgGCC dependency: floating point
MPC1.3.1multiprecision.orgGCC dependency: complex arithmetic

Base System

PackageVersionDownloadRole
Bash5.3gnu.org/bashShell
Coreutils9.10gnu.org/coreutilsls, cp, mv, cat, echo…
util-linux2.41.3kernel.org/util-linuxmount, blkid, lsblk, fdisk…
e2fsprogs1.47.3sf/e2fsprogsext4 filesystem tools
shadow4.19.3github/shadowuseradd, passwd, login
procps-ng4.0.6sf/procps-ngps, top, free
ncurses6.6invisible-island.netTerminal UI library
readline8.3gnu.org/readlineLine editing (used by bash)
zlib1.3.2zlib.netCompression library
bzip21.0.8sourceware.org/bzip2.bz2 compression
xz5.8.2tukaani.org/xz.xz / LZMA compression
tar1.35gnu.org/tarArchive tool
grep3.12gnu.org/grepPattern search
sed4.9gnu.org/sedStream editor
gawk5.3.2gnu.org/gawkAWK interpreter
make4.4.1gnu.org/makeBuild system
patch2.8gnu.org/patchApply diff patches
findutils4.10.0gnu.org/findutilsfind, locate, xargs
systemd259.1github/systemdInit system & service manager
GRUB2.14gnu.org/grubBootloader
Always Verify Before each build step, check that the tarball version matches exactly. Run sha256sum <tarball> and compare against the checksums published on the project's official download page.
Chapter 02

Preparing the Host System

We use Fedora 44 as the build host. The Fedora toolchain is modern and compatible with current package requirements. A minimal install plus development groups is all you need.

Install Required Host Packages

root# dnf groupinstall "Development Tools"
root# dnf install \
    bison flex gawk texinfo bc m4 \
    python3 perl wget curl \
    libstdc++-static glibc-static \
    xz bzip2 zlib-devel openssl-devel \
    libmpc-devel gmp-devel mpfr-devel

Verify Tool Versions

Fedora 44 ships a modern toolchain (GCC 15 / Binutils 2.44+) that exceeds all minimums for this guide. Run this check to confirm:

root# bash -c '
echo "Bash:     $(bash --version | head -1)"
echo "GCC:      $(gcc --version | head -1)"
echo "Binutils: $(ld --version | head -1)"
echo "Make:     $(make --version | head -1)"
echo "Python:   $(python3 --version)"
'
Recommended Take a VirtualBox snapshot labelled "host-ready" before proceeding. This gives you a clean rollback point.
Chapter 03

Partition & Mount the Target Disk

We need a dedicated partition for the new system. For a VM build, add a second virtual disk (30 GB minimum) and partition it as follows.

Partition Layout

PartitionSizeFilesystemMount
/dev/sdb1512 MBFAT32 (ESP)/boot/efi (UEFI systems)
/dev/sdb21 GBext4/boot
/dev/sdb3Remainderext4/ (root)
root# fdisk /dev/sdb
# Create GPT partition table, then partitions as above
# Type g for GPT, n for new partition, w to write

root# mkfs.fat -F32 /dev/sdb1
root# mkfs.ext4 /dev/sdb2
root# mkfs.ext4 /dev/sdb3

Set the LFS Variable and Mount

The $LFS variable is used throughout — it points to where the new system is being assembled.

root# export LFS=/mnt/lfs
root# mkdir -pv $LFS
root# mount /dev/sdb3 $LFS
root# mkdir -pv $LFS/boot
root# mount /dev/sdb2 $LFS/boot
root# mkdir -pv $LFS/boot/efi
root# mount /dev/sdb1 $LFS/boot/efi

Create the Sources Directory

root# mkdir -v $LFS/sources
root# chmod -v a+wt $LFS/sources
# Download all source tarballs to $LFS/sources
# You can use wget with the URLs from Chapter 01
Chapter 04

Build Environment Setup

Create the LFS Build User

Never build as root during the toolchain phase. Create a dedicated lfs user:

root# groupadd lfs
root# useradd -s /bin/bash -g lfs -m -k /dev/null lfs
root# passwd lfs
root# chown -v lfs $LFS/sources
root# chown -v lfs $LFS/tools

Configure the lfs User's Shell Environment

root# su - lfs
lfs$ cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
EOF
lfs$ cat > ~/.bashrc << "EOF"
set +h
umask 022
LFS=/mnt/lfs
LC_ALL=POSIX
LFS_TGT=$(uname -m)-lfs-linux-gnu
PATH=/usr/bin
if [ ! -L /bin ]; then PATH=/bin:$PATH; fi
PATH=$LFS/tools/bin:$PATH
CONFIG_SITE=$LFS/usr/share/config.site
export LFS LC_ALL LFS_TGT PATH CONFIG_SITE
EOF
lfs$ source ~/.bash_profile
Why set +h? This disables bash's hash table so the shell always searches PATH fresh. This is critical during the toolchain build — we need the shell to pick up the new tools we're building in $LFS/tools rather than the cached host paths.

Create the Toolchain Directories

root# mkdir -pv $LFS/tools
root# ln -sv $LFS/tools /tools

The symlink /tools$LFS/tools lets the toolchain build scripts use hardcoded /tools paths that work both on the host and, later, inside the chroot.

Chapter 05

Binutils — Pass 1

Binutils provides the assembler (as), linker (ld), and object file utilities. It must be compiled first because GCC and glibc run configure checks that rely on it.

Build Outside the Source Tree Binutils (and GCC) must be built in a separate build directory, not inside the extracted source tree. Failure to do this is one of the most common causes of cryptic build errors.
lfs$ cd $LFS/sources
lfs$ tar -xf binutils-2.46.0.tar.xz
lfs$ mkdir -v binutils-2.46.0/build
lfs$ cd binutils-2.46.0/build

lfs$ ../configure                     \
    --prefix=/tools                   \
    --with-sysroot=$LFS               \
    --target=$LFS_TGT                 \
    --disable-nls                      \
    --enable-gprofng=no                \
    --disable-werror                   \
    --enable-new-dtags                 \
    --enable-default-hash-style=gnu

lfs$ make -j$(nproc)
lfs$ make install

What --with-sysroot does

This tells binutils to look for libraries and headers under $LFS rather than the host root. Combined with --target=$LFS_TGT, this creates a cross-binutils that targets the new system.

Chapter 06

GCC — Pass 1

The first GCC pass produces a minimal C-only cross-compiler. It is intentionally limited — no shared libraries, no C++ support. Its sole purpose is to compile glibc.

lfs$ cd $LFS/sources
lfs$ tar -xf gcc-15.2.0.tar.xz
lfs$ cd gcc-15.2.0

# Unpack GCC prerequisites into the source tree
lfs$ tar -xf ../gmp-6.3.0.tar.xz  --transform 's/gmp-6.3.0/gmp/'
lfs$ tar -xf ../mpfr-4.2.2.tar.xz --transform 's/mpfr-4.2.2/mpfr/'
lfs$ tar -xf ../mpc-1.3.1.tar.gz  --transform 's/mpc-1.3.1/mpc/'

# x86_64: limit library search to lib64 only
lfs$ case $(uname -m) in
  x86_64)
    sed -e 's/m64=/m64=/' \
        -e 's@/lib/ld@/lib64/ld@g' \
        -i gcc/config/i386/t-linux64
  ;;
esac

lfs$ mkdir -v build && cd build

lfs$ ../configure                       \
    --target=$LFS_TGT                   \
    --prefix=/tools                     \
    --with-glibc-version=2.43           \
    --with-sysroot=$LFS                 \
    --with-newlib                        \
    --without-headers                   \
    --enable-default-pie                \
    --enable-default-ssp                \
    --disable-nls                        \
    --disable-shared                    \
    --disable-multilib                  \
    --disable-threads                   \
    --disable-libatomic                 \
    --disable-libgomp                   \
    --disable-libquadmath               \
    --disable-libssp                    \
    --disable-libvtv                    \
    --disable-libstdcxx                 \
    --enable-languages=c,c++

lfs$ make -j$(nproc)
lfs$ make install

# Create a full-featured limits.h (needed for glibc build)
lfs$ cat gcc/limitx.h gcc/glimits.h gcc/limity.h > \
  `dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/include/limits.h
Chapter 07

Linux Kernel Headers

The C library needs the kernel's API headers to know which system calls are available. We install them but do not build the kernel yet — that comes later.

lfs$ cd $LFS/sources
lfs$ tar -xf linux-7.1.5.tar.xz
lfs$ cd linux-7.1.5

lfs$ make mrproper

lfs$ make headers
lfs$ find usr/include -name '.*' -delete
lfs$ rm usr/include/Makefile
lfs$ cp -rv usr/include $LFS/usr
Note make mrproper ensures the source tree is pristine. Never skip this when working with kernel source trees.
Chapter 08

Glibc — The GNU C Library

This is the most critical and time-consuming build step. Glibc is the interface between your programs and the Linux kernel. Every program on your system will be linked against it.

lfs$ cd $LFS/sources
lfs$ tar -xf glibc-2.43.tar.xz
lfs$ cd glibc-2.43

# Ensure the dynamic linker looks in /lib64 on x86_64
lfs$ case $(uname -m) in
    i?86) ln -sfv ld-linux.so.2 $LFS/lib/ld-lsb.so.3 ;;
  x86_64) ln -sfv /lib/ld-linux-x86-64.so.2 $LFS/lib64
          ln -sfv /lib/ld-linux-x86-64.so.2 $LFS/lib64/ld-lsb-x86-64.so.3 ;;
esac

lfs$ patch -Np1 -i ../glibc-2.43-fhs-1.patch

lfs$ mkdir -v build && cd build

lfs$ echo "rootsbindir=/usr/sbin" > configparms

lfs$ ../configure                             \
    --prefix=/usr                             \
    --host=$LFS_TGT                           \
    --build=$(../scripts/config.guess)        \
    --enable-kernel=5.4                       \
    --with-headers=$LFS/usr/include           \
    --disable-nscd                            \
    libc_cv_slibdir=/usr/lib

lfs$ make -j$(nproc)
lfs$ make DESTDIR=$LFS install

# Fix the dynamic linker path in the GCC specs file
lfs$ sed '/RTLDLIST=/s@/usr@@g' -i $LFS/usr/bin/ldd

Sanity Check

This is critical. Test that the cross-compiler can actually produce executables that will run on the target:

lfs$ echo 'int main(){}' | $LFS_TGT-gcc -xc -
lfs$ readelf -l a.out | grep ld-linux
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
lfs$ rm -v a.out
Stop if this check fails The interpreter path must point into /lib64/ or /lib/ — not the host's path. If it shows your host system's path, something went wrong in the glibc configure step. Do not proceed until this is correct.
Chapter 09

Libstdc++ — Pass 1

Libstdc++ is GCC's C++ standard library. We install a pass-1 version now (from the same GCC source tree) so that subsequent packages with C++ components can build. The full version is reinstalled in GCC pass 2.

lfs$ cd $LFS/sources/gcc-15.2.0
lfs$ mkdir -v build-libstdc++ && cd build-libstdc++

lfs$ ../libstdc++-v3/configure             \
    --host=$LFS_TGT                       \
    --build=$(../config.guess)            \
    --prefix=/usr                         \
    --disable-multilib                    \
    --disable-nls                         \
    --disable-libstdcxx-pch              \
    --with-gxx-include-dir=/tools/$LFS_TGT/include/c++/15.2.0

lfs$ make -j$(nproc)
lfs$ make DESTDIR=$LFS install

# Remove the libtool archive files — they cause trouble later
lfs$ rm -v $LFS/usr/lib/lib{stdc++{,exp},supc++}.la
Chapter 10

Binutils — Pass 2

Now that glibc is in place, we rebuild binutils targeting the new system's headers and libraries. This replaces the pass-1 cross-binutils with a proper version.

lfs$ cd $LFS/sources/binutils-2.46.0
lfs$ rm -rf build && mkdir build && cd build

lfs$ ../configure                           \
    --prefix=/usr                           \
    --build=$(../config.guess)              \
    --host=$LFS_TGT                         \
    --disable-nls                           \
    --enable-shared                         \
    --enable-gprofng=no                     \
    --disable-werror                        \
    --enable-64-bit-bfd                     \
    --enable-new-dtags                      \
    --enable-default-hash-style=gnu

lfs$ make -j$(nproc)
lfs$ make DESTDIR=$LFS install

# Remove static libraries not needed at runtime
lfs$ rm -v $LFS/usr/lib/lib{bfd,ctf,ctf-nobfd,opcodes,sframe}.{a,la}
Chapter 11

GCC — Pass 2

The full GCC, with C, C++, shared libraries, threading support, and all standard features enabled. This replaces the minimal pass-1 compiler.

lfs$ cd $LFS/sources/gcc-15.2.0
lfs$ rm -rf build && mkdir build && cd build

lfs$ ../configure                           \
    --build=$(../config.guess)              \
    --host=$LFS_TGT                         \
    --target=$LFS_TGT                       \
    LDFLAGS_FOR_TARGET=-L$PWD/$LFS_TGT/libgcc  \
    --prefix=/usr                           \
    --with-build-sysroot=$LFS               \
    --enable-default-pie                    \
    --enable-default-ssp                    \
    --disable-nls                           \
    --disable-multilib                      \
    --disable-libatomic                     \
    --disable-libgomp                       \
    --disable-libquadmath                   \
    --disable-libsanitizer                  \
    --disable-libssp                        \
    --disable-libvtv                        \
    --enable-languages=c,c++

lfs$ make -j$(nproc)
lfs$ make DESTDIR=$LFS install

# Create a versioned cc symlink
lfs$ ln -sv gcc $LFS/usr/bin/cc
Toolchain complete You now have a self-hosting cross-compilation toolchain in $LFS. Everything from this point forward is compiled using tools that live inside the target system, not the host.
Chapter 12

Entering the Chroot Environment

From here we switch from building for the target system to building inside it. The chroot gives us an isolated environment where the new system's tools are the only tools available.

Create the Full Directory Structure

root# mkdir -pv $LFS/{dev,proc,sys,run}
root# mkdir -pv $LFS/usr/{bin,lib,sbin}
root# mkdir -pv $LFS/{boot,home,mnt,opt,srv}
root# mkdir -pv $LFS/etc/{opt,sysconfig}
root# mkdir -pv $LFS/lib/firmware
root# mkdir -pv $LFS/media/{floppy,cdrom}
root# mkdir -pv $LFS/usr/{include,src}
root# mkdir -pv $LFS/usr/share/{color,dict,doc,info,locale,man}
root# mkdir -pv $LFS/usr/share/{misc,terminfo,zoneinfo}
root# mkdir -pv $LFS/usr/share/man/man{1..8}
root# mkdir -pv $LFS/var/{cache,local,log,mail,opt,spool}
root# mkdir -pv $LFS/var/lib/{color,misc,locate}

# Create FHS compatibility symlinks
root# for d in bin lib sbin; do
  ln -sv usr/$d $LFS/$d
done
root# ln -sv usr/lib $LFS/lib64

root# install -dv -m 0750 $LFS/root
root# install -dv -m 1777 $LFS/tmp $LFS/var/tmp

Mount Virtual Filesystems

root# mount -v --bind /dev $LFS/dev
root# mount -vt devpts devpts -o gid=5,mode=0620 $LFS/dev/pts
root# mount -vt proc proc $LFS/proc
root# mount -vt sysfs sysfs $LFS/sys
root# mount -vt tmpfs tmpfs $LFS/run
root# if [ -h $LFS/dev/shm ]; then
  install -v -d -m 1777 $LFS$(realpath /dev/shm)
else
  mount -vt tmpfs -o nosuid,nodev tmpfs $LFS/dev/shm
fi

Enter the Chroot

root# chroot "$LFS" /usr/bin/env -i   \
    HOME=/root                         \
    TERM="$TERM"                      \
    PS1='(lfs chroot) \u:\w\$ '       \
    PATH=/usr/bin:/usr/sbin            \
    MAKEFLAGS="-j$(nproc)"             \
    TESTSUITEFLAGS="-j$(nproc)"       \
    /bin/bash --login

The prompt changes to (lfs chroot). You are now inside your new system.

Chapter 13

Core Package Builds

Inside the chroot, we now build all the essential packages that make up a usable system. Below is the required build order — dependencies flow downward.

Inside chroot from here on All commands in this chapter run inside the chroot environment entered in Chapter 12.

1. man-pages

root# tar -xf man-pages-6.17.tar.xz
root# rm -v man-pages-6.17/man3/crypt*.3
root# make -C man-pages-6.17 prefix=/usr install

2. zlib

root# cd zlib-1.3.2 && ./configure --prefix=/usr
root# make -j$(nproc) && make install
root# rm -fv /usr/lib/libz.a

3. bzip2

root# cd bzip2-1.0.8
root# patch -Np1 -i ../bzip2-1.0.8-install_docs-1.patch
root# sed -i 's@\(ln -s -f \)$(PREFIX)/bin/@\1@' Makefile
root# sed -i "s@(PREFIX)/man@(PREFIX)/share/man@g" Makefile
root# make -f Makefile-libbz2_so
root# make clean && make && make PREFIX=/usr install
root# cp -av libbz2.so.* /usr/lib && ln -sv libbz2.so.1.0.8 /usr/lib/libbz2.so

4. xz

root# cd xz-5.8.2
root# ./configure --prefix=/usr --disable-static --docdir=/usr/share/doc/xz-5.8.2
root# make -j$(nproc) && make install

5. ncurses

root# cd ncurses-6.6
root# ./configure                        \
    --prefix=/usr                      \
    --mandir=/usr/share/man            \
    --with-shared                      \
    --without-debug                    \
    --without-normal                   \
    --with-cxx-shared                  \
    --enable-pc-files                  \
    --with-pkg-config-libdir=/usr/lib/pkgconfig
root# make -j$(nproc) && make install
root# ln -sv libncursesw.so /usr/lib/libncurses.so

6. readline

root# cd readline-8.3
root# ./configure --prefix=/usr          \
                 --disable-static        \
                 --with-curses           \
                 --docdir=/usr/share/doc/readline-8.3
root# make -j$(nproc) SHLIB_LIBS="-lncursesw"
root# make install SHLIB_LIBS="-lncursesw"

7. bash

root# cd bash-5.3
root# ./configure                         \
    --prefix=/usr                       \
    --without-bash-malloc               \
    --with-installed-readline           \
    --docdir=/usr/share/doc/bash-5.3
root# make -j$(nproc) && make install
root# ln -sv bash /usr/bin/sh

8. coreutils

root# cd coreutils-9.10
root# patch -Np1 -i ../coreutils-9.10-i18n-2.patch
root# ./configure                                   \
    --prefix=/usr                                 \
    --enable-no-install-program=kill,uptime       \
    --docdir=/usr/share/doc/coreutils-9.10
root# make -j$(nproc) && make install
root# mv -v /usr/bin/chroot /usr/sbin
Chapter 14

System-Level Packages

The following packages form the system infrastructure layer. Build them in order.

util-linux

root# cd util-linux-2.41.3
root# ./configure                             \
    ADJTIME_PATH=/var/lib/hwclock/adjtime    \
    --bindir=/usr/bin                        \
    --libdir=/usr/lib                        \
    --runstatedir=/run                       \
    --sbindir=/usr/sbin                      \
    --disable-chfn-chsh                     \
    --disable-login                         \
    --disable-nologin                       \
    --disable-su                            \
    --disable-setpriv                       \
    --disable-runuser                       \
    --disable-pylibmount                    \
    --disable-liblastlog2                   \
    --disable-static                        \
    --without-python                        \
    --docdir=/usr/share/doc/util-linux-2.41.3
root# make -j$(nproc) && make install

e2fsprogs

root# cd e2fsprogs-1.47.3 && mkdir build && cd build
root# ../configure                        \
    --prefix=/usr                       \
    --sysconfdir=/etc                   \
    --enable-elf-shlibs                 \
    --disable-libblkid                  \
    --disable-libuuid                   \
    --disable-uuidd                     \
    --disable-fsck
root# make -j$(nproc) && make install
root# rm -fv /usr/lib/{libcom_err,libe2p,libext2fs,libss}.a

shadow

root# cd shadow-4.19.3
root# sed -i 's/groups$(EXEEXT) //' src/Makefile.in
root# find man -name Makefile.in -exec sed -i 's/groups\.1 //' {} \;
root# ./configure                         \
    --sysconfdir=/etc                   \
    --disable-static                    \
    --with-{b,y}crypt                   \
    --with-group-name-max-length=32
root# make -j$(nproc) && make exec_prefix=/usr install
root# pwconv && grpconv
root# passwd root

procps-ng

root# cd procps-ng-4.0.6
root# ./configure                        \
    --prefix=/usr                      \
    --docdir=/usr/share/doc/procps-ng-4.0.6 \
    --disable-static                   \
    --disable-kill                     \
    --enable-watch8bit                 \
    --with-systemd
root# make -j$(nproc) && make install

Remaining Utilities

Build the following packages using the standard pattern ./configure --prefix=/usr && make -j$(nproc) && make install. Refer to each project's own README for flags specific to your use case:

Chapter 15

Building the Linux Kernel 7.1.5

The kernel is the final major compile. We use make defconfig as a starting point and then enable a minimal set of options required for a bootable system.

root# cd /sources/linux-7.1.5
root# make mrproper

# Start with defconfig for your architecture
root# make defconfig

# Open the interactive config menu (optional, recommended for learning)
root# make menuconfig

Essential Kernel Options

Use menuconfig to verify or enable these options. The CONFIG_ names are searchable with /:

Config SymbolSettingReason
CONFIG_EXPERTyUnlocks advanced options
CONFIG_SYSFSyRequired for udev/systemd
CONFIG_PROC_FSyRequired for /proc
CONFIG_TMPFSyRequired for /tmp, /run
CONFIG_UNIXyUnix domain sockets (systemd)
CONFIG_INOTIFY_USERysystemd file watching
CONFIG_EXT4_FSyRoot filesystem
CONFIG_CGROUPSysystemd cgroup management
CONFIG_NAMESPACESysystemd namespace isolation
CONFIG_NETyNetworking
CONFIG_INETyTCP/IP stack
CONFIG_BLK_DEV_SDySCSI/SATA disk support
CONFIG_ATAyATA controllers
CONFIG_EFI_STUByUEFI boot support
# Build the kernel, modules, and install
root# make -j$(nproc)
root# make modules_install

# Install the kernel image and System.map
root# cp -iv arch/x86/boot/bzImage /boot/vmlinuz-7.1.5-forge
root# cp -iv System.map /boot/System.map-7.1.5
root# cp -iv .config /boot/config-7.1.5
Kernel modules For a VM, you can safely build most things into the kernel (=y) rather than as modules (=m). This simplifies the initramfs situation considerably.
Chapter 16

System Configuration Files

/etc/fstab

root# cat > /etc/fstab << "EOF"
# <device>        <mountpoint> <type>  <options>         <dump> <pass>
/dev/sdb3          /            ext4    defaults           1      1
/dev/sdb2          /boot        ext4    defaults           0      2
/dev/sdb1          /boot/efi    vfat    umask=0077         0      1
tmpfs              /tmp         tmpfs   defaults,nosuid,nodev 0   0
EOF
Use UUIDs in production Replace /dev/sdbX with UUIDs from blkid to ensure correct mounting regardless of device enumeration order. Example: UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Hostname

root# echo "forge" > /etc/hostname

Hosts File

root# cat > /etc/hosts << "EOF"
127.0.0.1  localhost
127.0.1.1  forge
::1        localhost ip6-localhost ip6-loopback
EOF

Timezone

root# ln -sfv /usr/share/zoneinfo/UTC /etc/localtime
# Or for a specific timezone:
root# ln -sfv /usr/share/zoneinfo/Asia/Kolkata /etc/localtime

Locale

root# localedef -i en_US -f UTF-8 en_US.UTF-8
root# cat > /etc/locale.conf << "EOF"
LANG=en_US.UTF-8
EOF

Input Key Bindings

root# cat > /etc/inputrc << "EOF"
set horizontal-scroll-mode Off
set meta-flag On
set input-meta On
set convert-meta Off
set output-meta On
set bell-style none
"\eOd": backward-word
"\eOc": forward-word
"\e[1~": beginning-of-line
"\e[4~": end-of-line
"\e[5~": beginning-of-history
"\e[6~": end-of-history
"\e[3~": delete-char
"\e[2~": quoted-insert
EOF

Shells

root# cat > /etc/shells << "EOF"
/bin/sh
/bin/bash
EOF

Placeholder OS identity

Minimal stub so the system is not anonymous before Chapter 20. Replace with the full Forge kit later.

root# cat > /etc/os-release << "EOF"
NAME="Forge Linux"
PRETTY_NAME="Forge Linux 0.1.0 (anvil)"
ID=forge
VERSION_ID="0.1.0"
VERSION_CODENAME=anvil
HOME_URL="https://forge.example/"
EOF
root# ln -sfv /etc/os-release /usr/lib/os-release
Chapter 17

systemd 259.1

systemd replaces SysVinit and provides the init system, service manager, journal, udev, network management, and much more. It has a significant number of dependencies — build them first.

Dependencies to build first

  1. libcap 2.77 — POSIX capabilities library (configure with --prefix=/usr --disable-static)
  2. libxcrypt 4.5.2 — Modern crypt() implementation for password hashing
  3. cryptsetup 2.8.0 — Disk encryption (optional but recommended)
  4. kmod 34.2 — Kernel module loading (modprobe, lsmod)
  5. libunistring 1.3 — Unicode string library
  6. libffi 3.5.2 — Foreign function interface
  7. Python 3.14.3 — Required by systemd's test suite and tools

Build systemd with Meson

root# cd /sources/systemd-259.1
root# pip3 install jinja2 pyelftools

root# meson setup --prefix=/usr              \
             --buildtype=release            \
             -Ddefault-dnssec=no             \
             -Dfirstboot=false               \
             -Dinstall-tests=false           \
             -Dldconfig=false                 \
             -Dman=disabled                   \
             -Dsysusers=false                 \
             -Drpmmacrosdir=no                \
             -Dhomed=disabled                 \
             -Duserdb=false                   \
             -Dmode=release                   \
             -Dpamconfdir=no                  \
             -Ddev-kvm-mode=0660              \
             -Dnobody-group=nogroup           \
             -Dsysupdate=disabled             \
             -Dukify=disabled                 \
             build

root# ninja -C build && ninja -C build install

Post-install Configuration

# Disable graphical login (not needed for minimal system)
root# systemctl set-default multi-user.target

# Create machine-id (required by systemd-journald)
root# systemd-machine-id-setup

# Enable basic services
root# systemctl enable systemd-networkd
root# systemctl enable systemd-resolved

Network Configuration via systemd-networkd

root# cat > /etc/systemd/network/10-eth0.network << "EOF"
[Match]
Name=eth0

[Network]
DHCP=yes
DNS=1.1.1.1
DNS=8.8.8.8
EOF
Chapter 18

GRUB 2.14 — The Bootloader

GRUB (Grand Unified Bootloader) is what the firmware hands control to after POST. It then loads the kernel and passes it an initial set of parameters.

Build GRUB

root# cd /sources/grub-2.14

root# ./configure                           \
    --prefix=/usr                         \
    --sysconfdir=/etc                     \
    --disable-efiemu                      \
    --enable-grub-mkfont                  \
    --with-platform=efi                   \
    --target=x86_64                       \
    --disable-werror

root# make -j$(nproc) && make install

Install GRUB to the ESP

# UEFI install (recommended for all modern systems)
root# grub-install                          \
    --target=x86_64-efi                   \
    --efi-directory=/boot/efi             \
    --bootloader-id=LFS                   \
    --recheck

# Legacy BIOS install (if your VM is not UEFI)
root# grub-install --target=i386-pc /dev/sdb

Generate grub.cfg

root# cat > /boot/grub/grub.cfg << "EOF"
# GRUB 2 configuration for Forge Linux
set default=0
set timeout=5

insmod part_gpt
insmod ext2
insmod fat

# Load EFI video support
if [ x$grub_platform = xxefi ]; then
  insmod linuxefi
fi

menuentry "Forge Linux 0.1 — Linux 7.1.5" {
    linux   /boot/vmlinuz-7.1.5-forge root=/dev/sdb3 rw quiet
    # For UEFI: linuxefi /boot/vmlinuz-7.1.5-forge root=/dev/sdb3 rw quiet
}
EOF
UUID-based root Replace root=/dev/sdb3 with root=UUID=$(blkid -s UUID -o value /dev/sdb3) to make the config portable across disk re-enumeration.
Chapter 19

First Boot

Before rebooting, exit the chroot cleanly and unmount all virtual filesystems.

Exit and Unmount

# Inside the chroot
root# logout

# Back on the Fedora host
root# umount -v $LFS/dev/pts
root# umount -v $LFS/dev
root# umount -v $LFS/run
root# umount -v $LFS/proc
root# umount -v $LFS/sys
root# umount -v $LFS/boot/efi
root# umount -v $LFS/boot
root# umount -v $LFS

Boot the New System

  1. In VirtualBox/GNOME Boxes, change the VM boot order to boot from /dev/sdb first
  2. Reboot the virtual machine
  3. GRUB should appear with the "Forge Linux 0.1 — Linux 7.1.5" menu entry
  4. Select it and watch the kernel boot messages
  5. systemd should hand off to a login prompt on tty1
  6. Log in as root with the password you set in Chapter 14

Verify the System

root# uname -a
Linux forge 7.1.5 #1 SMP ... x86_64 GNU/Linux

root# cat /etc/os-release
NAME="Forge Linux"
PRETTY_NAME="Forge Linux 0.1.0 (anvil)"
ID=forge
VERSION_ID="0.1.0"

root# systemctl status
● forge
    State: running
    Units: 168 loaded (...)
    Jobs: 0 queued
  Failed: 0 units

root# ip link
root# ping -c3 1.1.1.1
Congratulations You have built a complete Linux system from source code. Every binary on this system was compiled by tools you compiled, linked against a C library you compiled, running on a kernel you configured and built. That's the full stack.

What Next?

Your system boots. Now make it yours — continue with Chapters 20–28:

Chapter 20

Brand Identity — Own Your Distro

A bootable rootfs is not yet a distribution. A distribution has a name people say out loud, a legal identity, consistent strings in every UI surface, and a versioning story. This chapter defines Forge Linux as the default brand for the rest of the guide. Replace the kit once; reuse it everywhere.

F
Forge Linux
BUILT FROM SOURCE · OWNED BY YOU

Identity fields

FieldValue
NAMEForge Linux
IDforge
VERSION_ID0.1.0
VERSION_CODENAMEanvil
PRETTY_NAMEForge Linux 0.1.0 (anvil)
HOME_URLhttps://forge.example/
BUG_REPORT_URLhttps://forge.example/bugs
SUPPORT_URLhttps://forge.example/docs
VENDORYour Name / Org
ANSI_COLOR1;32

Naming rules

  • ID — lowercase, no spaces (forge)
  • NAME — human-facing product name
  • Codenames — optional per release (anvil → bellows → crucible…)
  • Avoid trademarks — do not embed Ubuntu, Red Hat, Fedora, Debian in ID/NAME
  • Legal — branding does not relicense GPL components; keep COPYING/NOTICE
Tip Pick a name that works for domain, Git org, and GPG identity. Renaming after packages ship is painful.

Single source of truth — identity kit

Create this file on the host and copy it into the target. Every branding script sources it.

root# mkdir -pv /opt/forge-brand
root# cat > /opt/forge-brand/identity.env << 'EOF'
# === Forge Linux identity kit — edit once ===
export DISTRO_NAME="Forge Linux"
export DISTRO_NAME_SHORT="Forge"
export DISTRO_ID="forge"
export DISTRO_VERSION="0.1.0"
export DISTRO_CODENAME="anvil"
export DISTRO_PRETTY="${DISTRO_NAME} ${DISTRO_VERSION} (${DISTRO_CODENAME})"
export DISTRO_HOME_URL="https://forge.example/"
export DISTRO_BUG_URL="https://forge.example/bugs"
export DISTRO_SUPPORT_URL="https://forge.example/docs"
export DISTRO_VENDOR="Your Name or Organization"
export DISTRO_ANSI_COLOR="1;32"
export DISTRO_LOGO="forge-logo"
export DISTRO_KERNEL_LOCALVERSION="-forge"
export DISTRO_HOSTNAME="forge"
export DISTRO_GRUB_ID="Forge"
export DISTRO_ISO_LABEL="FORGE_0_1"
export DISTRO_CONTACT="maintainers@forge.example"
export DISTRO_COLOR_PRIMARY="#4ade80"
export DISTRO_COLOR_BG="#0d0f12"
export DISTRO_TAGLINE="Built from source. Owned by you."
EOF
root# source /opt/forge-brand/identity.env
root# mkdir -pv $LFS/usr/share/forge
root# cp -v /opt/forge-brand/identity.env $LFS/usr/share/forge/

Brand asset checklist

On-target brand directory layout

/usr/share/forge/
  identity.env
  branding/
    logo.svg
    logo-dark.svg
    mark.png
    grub-bg.png
    plymouth/
    wallpapers/
  NOTICE                     # third-party license rollup
/usr/share/pixmaps/forge-logo.png
/usr/share/backgrounds/forge/
/etc/os-release
/etc/forge-release
Legal note Your brand wraps the system; it does not relicense the kernel or GPL userspace. Ship package COPYING files and a top-level /usr/share/forge/NOTICE listing Linux, glibc, GCC runtime, systemd, GRUB, and major GNU tools.
Chapter 21

OS Identity Files

These files answer “what OS is this?” for hostnamectl, containers, installers, and humans. Apply inside the chroot after sourcing identity.env.

/etc/os-release (required)

root# source /usr/share/forge/identity.env
root# cat > /etc/os-release << EOF
NAME="${DISTRO_NAME}"
PRETTY_NAME="${DISTRO_PRETTY}"
ID=${DISTRO_ID}
ID_LIKE=lfs
VERSION="${DISTRO_VERSION} (${DISTRO_CODENAME})"
VERSION_ID="${DISTRO_VERSION}"
VERSION_CODENAME=${DISTRO_CODENAME}
BUILD_ID=$(date -u +%Y%m%d)
HOME_URL="${DISTRO_HOME_URL}"
DOCUMENTATION_URL="${DISTRO_SUPPORT_URL}"
SUPPORT_URL="${DISTRO_SUPPORT_URL}"
BUG_REPORT_URL="${DISTRO_BUG_URL}"
PRIVACY_POLICY_URL="${DISTRO_HOME_URL}privacy"
LOGO=${DISTRO_LOGO}
ANSI_COLOR="${DISTRO_ANSI_COLOR}"
VENDOR_NAME="${DISTRO_VENDOR}"
EOF
root# ln -sfv /etc/os-release /usr/lib/os-release

/etc/lsb-release (compatibility)

root# cat > /etc/lsb-release << EOF
DISTRIB_ID=${DISTRO_NAME_SHORT}
DISTRIB_RELEASE=${DISTRO_VERSION}
DISTRIB_CODENAME=${DISTRO_CODENAME}
DISTRIB_DESCRIPTION="${DISTRO_PRETTY}"
EOF

/etc/forge-release

root# cat > /etc/forge-release << EOF
${DISTRO_PRETTY}
Built: $(date -u +%Y-%m-%dT%H:%MZ)
Kernel target: 7.1.5
Toolchain: GCC 15.2.0 / glibc 2.43 / Binutils 2.46.0
EOF

Login banners — /etc/issue & MOTD

root# cat > /etc/issue << EOF
${DISTRO_NAME} ${DISTRO_VERSION} (\\n) (\\l)
${DISTRO_TAGLINE}

EOF
root# cp -v /etc/issue /etc/issue.net

root# cat > /etc/motd << EOF

  ███████╗ ██████╗ ██████╗  ██████╗ ███████╗
  ██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝
  █████╗  ██║   ██║██████╔╝██║  ███╗█████╗
  ██╔══╝  ██║   ██║██╔══██╗██║   ██║██╔══╝
  ██║     ╚██████╔╝██║  ██║╚██████╔╝███████╗
  ╚═╝      ╚═════╝ ╚═╝  ╚═╝ ╚═════╝ ╚══════╝
  ${DISTRO_PRETTY}
  ${DISTRO_TAGLINE}
  Docs: ${DISTRO_SUPPORT_URL}

EOF

root# cat > /etc/profile.d/forge-motd.sh << 'EOF'
if [ -n "$PS1" ] && [ -z "$FORGE_MOTD_SHOWN" ]; then
  export FORGE_MOTD_SHOWN=1
  echo "Kernel: $(uname -r)  |  $(uptime -p 2>/dev/null || true)"
fi
EOF

Hostname & kernel localversion

root# echo "$DISTRO_HOSTNAME" > /etc/hostname
root# hostnamectl set-hostname "$DISTRO_HOSTNAME"
root# hostnamectl set-hostname --pretty "$DISTRO_PRETTY"

# When building the kernel (Ch. 15):
root# cd /sources/linux-7.1.5
root# scripts/config --set-str LOCALVERSION "-forge"
root# make -j$(nproc) && make modules_install
root# cp -iv arch/x86/boot/bzImage /boot/vmlinuz-7.1.5-forge
# uname -r → 7.1.5-forge
Containers OCI labels should mirror os-release: org.opencontainers.image.title, .version, .vendor, .source.
Chapter 22

Visual Branding

Make boot and login intentional: GRUB theme, console, optional Plymouth, terminal logos.

GRUB theme + branded menu

root# source /usr/share/forge/identity.env
root# mkdir -pv /boot/grub/themes/forge
root# cp /usr/share/forge/branding/grub-bg.png /boot/grub/themes/forge/background.png

root# cat > /boot/grub/themes/forge/theme.txt << 'EOF'
desktop-image: "background.png"
title-text: "Forge Linux"
title-color: "#4ade80"
message-color: "#e8edf5"
+ boot_menu {
  left = 15%; width = 70%; top = 30%; height = 40%
  item_color = "#c8d0e0"
  selected_item_color = "#0d0f12"
  item_height = 28; item_padding = 8; item_spacing = 6
}
EOF

root# ROOT_UUID=$(blkid -s UUID -o value /dev/sdb3)
root# cat > /boot/grub/grub.cfg << EOF
set default=0
set timeout=5
set gfxmode=auto
insmod all_video
insmod gfxterm
insmod png
insmod part_gpt
insmod ext2
terminal_output gfxterm
set theme=/boot/grub/themes/forge/theme.txt
export theme

menuentry "${DISTRO_NAME} ${DISTRO_VERSION} — Linux 7.1.5" {
    linux /boot/vmlinuz-7.1.5-forge root=UUID=${ROOT_UUID} rw quiet
}
menuentry "${DISTRO_NAME} (recovery)" {
    linux /boot/vmlinuz-7.1.5-forge root=UUID=${ROOT_UUID} rw single
}
EOF

Plymouth (graphical splash)

Build Plymouth via BLFS when DRM/KMS + initramfs exist. Theme skeleton:

/usr/share/plymouth/themes/forge/forge.plymouth
[Plymouth Theme]
Name=Forge
Description=Forge Linux boot splash
ModuleName=script

[script]
ImageDir=/usr/share/plymouth/themes/forge
ScriptFile=/usr/share/plymouth/themes/forge/forge.script
root# plymouth-set-default-theme -R forge
# kernel cmdline: quiet splash — rebuild initramfs after install

fastfetch / neofetch ASCII

root# cat > /usr/share/forge/ascii.txt << 'EOF'
      #####
     ##   ##     Forge Linux
     ##         Built from source
     ##  ###
     ##   ##
      #####
EOF
# fastfetch -l /usr/share/forge/ascii.txt

forge-apply-brand helper

root# cat > /usr/sbin/forge-apply-brand << 'EOF'
#!/bin/bash
set -euo pipefail
. /usr/share/forge/identity.env
# regenerate os-release, issue, motd, hostname from identity.env (Ch. 21)
echo "$DISTRO_HOSTNAME" > /etc/hostname
echo "Applied ${DISTRO_PRETTY}"
EOF
root# chmod 755 /usr/sbin/forge-apply-brand
Chapter 23

Package Manager — From Tarballs to a Distro

Without packages you have a golden image you cannot update cleanly. Choose early:

A. Scripted ports tree

Per-package build scripts + manifest. Best learning path; start here for Forge 0.1.

B. Binary packages

Adopt pacman, dpkg/apt, or rpm/dnf. Real distro UX; more work.

C. Image-based

OSTree / A/B images. Excellent for appliances; different model than classic LFS.

D. Hybrid

Immutable base + Flatpak/Nix for apps. Pragmatic for desktops.

Forge ports tree layout

/var/lib/forge/ports/
  toolchain/gcc/Pkgfile
  base/bash/Pkgfile
  base/coreutils/Pkgfile
  net/openssh/Pkgfile
  meta/base-system/Pkgfile
/var/cache/forge/sources/
/var/cache/forge/packages/

Minimal Pkgfile

name=hello
version=2.12.2
release=1
source=(https://ftp.gnu.org/gnu/hello/hello-${version}.tar.gz)
build() {
  cd "${name}-${version}"
  ./configure --prefix=/usr
  make -j$(nproc)
}
package() {
  make DESTDIR="$pkgdir" install
}

forgepkg sketch

root# cat > /usr/sbin/forgepkg << 'EOF'
#!/bin/bash
# Educational package helper — build | install | list
set -euo pipefail
PORTS=${PORTS:-/var/lib/forge/ports}
PKGDB=/var/lib/forge/db
CACHEDIR=/var/cache/forge/packages
mkdir -p "$PKGDB" "$CACHEDIR"
cmd=${1:-}; shift || true
case "$cmd" in
  build)  echo "Build port $1 (download, build(), package to $CACHEDIR)";;
  install) echo "Install package file $1; record in $PKGDB";;
  list)   cat "$PKGDB/installed" 2>/dev/null || true;;
  *) echo "Usage: forgepkg {build|install|list}"; exit 1;;
esac
EOF
root# chmod 755 /usr/sbin/forgepkg
Production Do not ship a half-finished solver. For binary repos, port pacman+makepkg, bootstrap rpm/dnf, or learn from Alpine abuild. Your edge is curation and branding.

Signed repository layout

https://repo.forge.example/
  forge/os/x86_64/
    forge-core.db
    forge-core.db.sig
    bash-5.3-1-x86_64.pkg.tar.zst
    bash-5.3-1-x86_64.pkg.tar.zst.sig
  iso/
    forge-0.1.0-anvil-x86_64.iso
    SHA256SUMS
    SHA256SUMS.asc

If you adopt pacman

  1. Build deps (openssl, curl, libarchive, gpgme) then pacman
  2. Configure /etc/pacman.conf with [forge-core] / [forge-extra]
  3. repo-add forge-core.db.tar.gz *.pkg.tar.zst
  4. Host over HTTPS; sign packages with distro GPG key (Ch. 27)
  5. Document pacman -Syu as the user upgrade path
Chapter 24

Userspace Stack — Make It Usable

After first boot you have a console OS. Prioritize by persona.

Tier 0 — Always (server / appliance base)

PackageWhy
iproute2ip, ss — real network control
OpenSSHRemote admin
curl / wgetFetch sources and updates
ca-certificatesTLS trust store
vim or nanoEdit configs
sudoLeast-privilege admin user
chrony / timesyncdCorrect clock
logrotateLog hygiene

Tier 1 — Developer box

git, strace, gdb, lsof, htop/btop, python3, tmux, man-db.

Tier 2 — Desktop (optional, BLFS)

  1. DRM/Mesa + libinput + Xorg or Wayland (Sway/Weston)
  2. Fonts + fontconfig
  3. PipeWire for audio
  4. XFCE (lighter) or GNOME/KDE
  5. Display manager: greetd, lightdm, or gdm
  6. Forge wallpapers + theme tokens from brand colors

Admin user

root# useradd -m -s /bin/bash -c "Forge Admin" forgeuser
root# passwd forgeuser
root# usermod -aG wheel forgeuser

nftables baseline

root# nft add table inet filter
root# nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
root# nft add rule inet filter input ct state established,related accept
root# nft add rule inet filter input iif lo accept
root# nft add rule inet filter input tcp dport 22 accept
Security Branding ≠ security. Disable root SSH passwords, track CVEs for your package set, and document a security contact in os-release / website.
Chapter 25

Live ISO & Disk Images

Users install from images. Plan to ship a live ISO, a raw/qcow2 disk image, and optionally an OCI base image from the same rootfs.

Host tooling (Fedora)

root# dnf install xorriso mtools squashfs-tools dosfstools \
  grub2-tools grub2-tools-extra grub2-efi-x64-modules \
  syslinux isomd5sum qemu-img edk2-ovmf

Pipeline

  1. Export clean rootfs (exclude /sources, /tools, caches)
  2. Build initramfs with live/overlay hooks
  3. mksquashfs rootfs → LiveOS/squashfs.img
  4. Stage ISO tree: kernel, initrd, GRUB EFI + BIOS, brand assets
  5. Hybrid ISO via xorriso
  6. SHA256 + GPG sign

Export rootfs

root# export LFS=/mnt/lfs
root# tar -C $LFS \
  --exclude=./sources --exclude=./tools \
  -cJf /var/tmp/forge-rootfs.tar.xz .

ISO skeleton

/var/tmp/forge-iso/
  EFI/BOOT/BOOTX64.EFI
  boot/grub/grub.cfg
  boot/grub/themes/forge/
  images/pxeboot/vmlinuz
  images/pxeboot/initrd.img
  LiveOS/squashfs.img
  LICENSE
  README.txt

xorriso (conceptual)

root# source /opt/forge-brand/identity.env
root# xorriso -as mkisofs \
  -iso-level 3 \
  -full-iso9660-filenames \
  -volid "$DISTRO_ISO_LABEL" \
  -appid "$DISTRO_NAME" \
  -publisher "$DISTRO_VENDOR" \
  -o /var/tmp/${DISTRO_ID}-${DISTRO_VERSION}-${DISTRO_CODENAME}-x86_64.iso \
  /var/tmp/forge-iso/
# Add eltorito BIOS + EFI alternate boot images per your GRUB embed setup
# Test: qemu-system-x86_64 -m 2048 -cdrom …  and UEFI with OVMF

VM / cloud image

root# qemu-img create -f raw forge-0.1.0.img 8G
root# losetup -fP forge-0.1.0.img
# partition, mkfs, rsync rootfs, grub-install, forge-apply-brand
# qemu-img convert -c -O qcow2 forge-0.1.0.img forge-0.1.0.qcow2

Checksums

root# cd /var/tmp
root# sha256sum forge-*.iso forge-*.qcow2 > SHA256SUMS
root# gpg --local-user release@forge.example \
  --detach-sign --armor SHA256SUMS
Chapter 26

Installer

An installer turns a rootfs into a product people can deploy without reading build chapters.

1. Shell installer

forge-install TUI: disk → partition → unpack → GRUB → password. Perfect for 0.1.

2. Calamares

Qt GUI + Forge branding pack — fastest polished installer path.

3. Custom GUI

Anaconda/Subiquity-class effort. Only if installer UX is the product.

4. Image flash

No installer — dd a preseeded image (appliances, SBCs, cloud).

Installer flow

  1. List block devices; destructive confirm
  2. GPT: ESP 512M, boot 1G, root rest (match Ch. 03)
  3. Format + mount at /mnt/target
  4. Unpack squashfs/rootfs
  5. chroot: forge-apply-brand, root password, locale, timezone
  6. grub-install + UUID-based grub.cfg
  7. Unmount; reboot
root# cat > /usr/sbin/forge-install << 'EOF'
#!/bin/bash
set -euo pipefail
. /usr/share/forge/identity.env 2>/dev/null || true
echo "=== ${DISTRO_NAME:-Forge Linux} installer ==="
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
read -r -p "Target disk (e.g. /dev/vda): " DISK
read -r -p "ERASE $DISK and install? type YES: " CONFIRM
[[ "$CONFIRM" == "YES" ]] || exit 1
# partition (sfdisk/parted), mkfs, rsync rootfs, grub, passwords …
echo "Install complete. Reboot into ${DISTRO_PRETTY:-Forge}."
EOF
root# chmod 755 /usr/sbin/forge-install

Calamares branding paths

/etc/calamares/branding/forge/
  branding.desc
  stylesheet.qss
  logo.png
  welcome.png

First-boot unit (cloud images)

root# cat > /etc/systemd/system/forge-firstboot.service << 'EOF'
[Unit]
Description=Forge Linux first boot
ConditionPathExists=!/var/lib/forge/firstboot-done
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/libexec/forge-firstboot
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF
root# systemctl enable forge-firstboot.service

First-boot expands disks, regenerates machine-id, injects SSH keys (cloud-init), sets hostname from metadata.

Chapter 27

Release Engineering

Shipping repeatedly is what makes a distro real.

Versioning

SchemeExampleWhen
SemVer + codename0.1.0 (anvil)Forge default
CalVer2026.08Monthly snapshots / rolling
Point release1.0 → 1.1Stable series with backports

Release checklist

Distro signing key

root# gpg --full-generate-key
# Identity: Forge Linux Release <release@forge.example>
root# gpg --export --armor release@forge.example \
  > forge-release.pubkey.asc
# Publish on HTTPS + fingerprint in every ISO README

Release notes template

# Forge Linux 0.1.0 (anvil) — 2026-08-03

## Highlights
- First public release
- Linux 7.1.5, GCC 15.2, glibc 2.43, systemd 259.1
- Branded live ISO + raw image

## Known issues
- No GUI in base ISO
- forgepkg is educational — prefer ports scripts

## Verify
sha256sum -c SHA256SUMS
gpg --verify SHA256SUMS.asc

## Upgrade
Clean install recommended for 0.x

CI outline

# on tag v*:
#   build-rootfs → build-iso → qemu-smoke → sign → publish

SBOM / auditability

Chapter 28

Distro Roadmap — Beyond the Guide

ReleaseCodenameGoals
0.1anvilBranded bootable system, SSH, docs, ISO alpha
0.2bellowsReal package manager + signed repo + installer beta
0.3crucibleDesktop spin and/or server hardening profile
1.0hammerStable promise, LTS kernel option, security policy

Product decisions

Docs site (brand surface)

Master checklist — “I have a distro”

You own this Rename Forge → your mark, change colors, point HOME_URL at your domain. Chapters 00–19 are the engineering spine; 20–28 are the product.
F
Forge Linux
END OF GUIDE · START OF YOUR DISTRO

Companions: LFS · BLFS · os-release(5) · kernel.org