Build a minimal Raspberry Pi OS from source

Raspberry Pi DIY

© Lead Image © Eduard Peter,

© Lead Image © Eduard Peter,

Article from Issue 245/2021

The detLFS project provides an ideal foundation for compiling Linux from source code, either to experience the fundamentals of how Linux works or to prepare an operating system for a project with very specific requirements.

The Linux From Scratch (LFS) project has been around for more than 20 years [1]. It does not deliver software as a result, but detailed instructions on how to create an executable Linux system from the source code itself. Why might you want to take this route? (1) You might be a masochist with too much time on your hands. (2) You have a project with very specific requirements. (3) You want to learn fundamentally how Linux works. The detLFS [2] project by Thomas Dettbarn answers these needs.

Unlike the pure theory of LFS, detLFS makes life far easier for anyone wanting to take on the challenge because the project provides a set of scripts instead of a manual. The scripts create a runnable, minimal Linux system for the Raspberry Pi almost without interaction. As a prerequisite you need a desktop Linux with the usual build tools (i.e., a compiler and make), as well as ImageMagick/Netpbm for your own kernel logo. These tools can be found on any normal developer's computer; if in doubt, install them from your package manager. They are typically pseudo-packages named build-essentials, or something similar.

Simply downloading the repository and running the scripts is boring and basically pointless, because nobody gains anything from the minimal system the process creates. Understanding the process and knowing how to adapt it to your own needs is far more important.

How Linux Works

Whether on a Raspberry Pi or on a supercomputer, Linux basically always works in the same way. First, the hardware boots the kernel from disk. This part depends heavily on the platform. Without firmware, also known as BIOS in the desktop area, nothing works.

After startup, the kernel initializes its subsystems and drivers then passes control to the init process by starting the /sbin/init program with process ID 1. On modern Linux systems, the systemd process adopts this ID shortly afterward. The init program then gradually starts all the application programs and processes that make up a system. The kernel itself remains in the background as a silent worker, stepping in whenever a program needs access to resources, such as during a program startup or for reading a file (Figure 1).

Figure 1: The launched user processes interact with the kernel.

A minimal system comprises three components: the firmware, a kernel, and at least one init program. For a Raspberry Pi, whose only task is to control and switch something, this setup might be enough. detLFS is not quite as minimal. In addition to the init program, standard Linux programs or honed down versions (e.g., ls, cp, etc.) are also present.

To create your own minimal system with very little overhead, a script first collects all the required program sources. Subsequently, a second script generates a cross compiler along with companion programs, which are needed because you want to create an executable kernel and application programs for the Raspberry Pi ARM architecture on an x86 system. The schematic flow is shown in Figure 2. All scripts need to run within a single session; otherwise, you will have to repeat the export commands, which are valid only within a single session.

Figure 2: The schematic flow for creating a system generated with detLFS.


For this project, I provide a fork of the original scripts on GitHub [3], mainly because various bugfixes and optimizations significantly speed up the download and the process of building the system. Furthermore, the fork supports all hardware variants of the Raspberry Pi, not just versions 2 and 3. You can retrieve the software with the commands:

$ git clone
$ rm -fr /data/projects/detLFS
$ cp -a pi-detLFS/detLFS /data/projects
$ cd /data/projects/detLFS

The last two commands copy the directory with the scripts to a suitable project directory and change to that location. If the directory is on a filesystem formatted with XFS or Btrfs, all of the later scripts run faster and they require less space because these filesystems only copy the references when duplicating files, not the data blocks.

After these preparations, run:

$ export BRANCH=rpi-4.19.y
$ ./ |& tee 0_getit.log

The optional first line specifies the kernel branch the script fetches from the repository (various lines of development exist in separate branches). Without the export command, the script uses the current default branch. Instead of setting the branch manually, you can alternatively copy the kernel configuration file .config from a running Raspberry Pi into the detLFS directory:

$ sudo modprobe configs
$ zcat /proc/config.gz > .config

The script in the earlier set of commands downloads about 1.1GB of compressed sources, which when unpacked add up to about twice that. For each download, the script creates a file in the Downloads/ directory that follows the pattern .detlfs.<xxx> (e.g., .detlfs.kernel) (Figure 3). If you want to download something again, maybe because you want to try out a different kernel version, delete the file in question beforehand. In any case, check the 0_getit.log file after execution – this applies in kind to all further steps.

Figure 3: The Downloads/ directory is used by the detLFS scripts as a cache for all required files.

Only the downloaded source packages end up in the Downloads/ directory. With the exception of the kernel, these are archives. The script immediately copies or unpacks the sources into the Sources. directory. The scripts downstream of this step then make use of them.


The second step in the workflow creates the tools that the downstream scripts need and relates primarily to the cross compiler, which runs on a desktop system (x86), but generates code for the ARM variant:

$ ./ | & tee 1_buildtools.log

Fortunately, the normal desktop compiler also builds this cross compiler in the most time-consuming step in the whole process. Ready-made cross compilers can be found online and from the Raspberry Pi Foundation, but users report in forums having difficulties with these tools time and time again. Therefore, the detLFS project prefers building the programs itself.

The computer I used in this project, an AMD Ryzen 5 2400G, is not necessarily a top performer; in fact, it is basically geared toward office work with its integrated GPU. The quad-core processor claims to be an eight-core CPU to the operating system because of hyperthreading. If all the cores are used (to find out, use the -j 8 option with make), the call described above takes about 18 minutes. If only one core is doing the work, it takes 62 minutes. However, hyperthreading doesn't offer too many benefits with this CPU because of the fairly small CPU cache: with four cores, the compiler run takes 22 minutes, which is only marginally longer than with the full core count.

Once created, the tools do their job no matter how many different kernel and system versions you want to try, so the runtime is only consumed once.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More