Linux From Scratch All articles
Technical Guides

Two Days, Zero Regrets: A Timed Experiment in Building LFS at Full Speed

Linux From Scratch
Two Days, Zero Regrets: A Timed Experiment in Building LFS at Full Speed

There's a familiar rhythm to a first Linux From Scratch build. You clear a Saturday, get through chapter five, lose Sunday to Glibc, and find yourself compiling Binutils on your lunch break the following Wednesday. Two weeks later, you're staring at a boot menu, wondering where the time went.

I decided to find out where it goes — and how much of it can be reclaimed without compromising the integrity of the system. The rules were simple: follow the book faithfully, log every package's build time, and deploy every legitimate optimization available. The result: a complete, functional LFS system in 41 hours of wall-clock time, spread across two working days.

Where the Hours Actually Go

Before optimizing anything, I needed data. Running the book sequentially on a modest six-core Ryzen 5 with 32 GB of RAM produced a telling breakdown. The temporary toolchain accounted for roughly a third of total build time, with Glibc alone consuming nearly two hours. The kernel configuration and compilation added another 90 minutes. The long tail — the dozens of smaller packages — quietly ate up most of the remainder.

The lesson from the log is consistent with what experienced builders already suspect: there is no single bottleneck. Speed comes from shaving minutes off of forty separate places, not from one dramatic fix.

Parallel Jobs: The Highest-Leverage Setting

The single most effective change is also the simplest. Setting MAKEFLAGS='-j$(nproc)' in the environment lets every package that honors Make's job control use all available cores. On my hardware, this alone cut total build time by more than half compared to single-threaded compilation.

Two cautions apply. First, memory: each parallel job can consume a gigabyte or more during Glibc and GCC, so a safe heuristic on a 32 GB machine is -j8 to -j12, not blindly -j$(nproc) on systems with many cores and little RAM. Second, a handful of packages in the book are sensitive to parallelism and may fail intermittently. When a build fails inexplicably, retrying single-threaded is the standard diagnostic — and a good habit to keep.

tmpfs: Compile in Memory, Not on Spinning Rust

If your build directory lives on an SSD, the gains here are modest. But pointing your build tree at a tmpfs — a filesystem backed by RAM — eliminates a surprising amount of I/O latency, particularly during the configure stages where thousands of small files are written and read.

On a machine with 32 GB of RAM, allocating 16 GB to tmpfs is comfortable, since the source archives and installed toolchain live elsewhere. Builders on memory-constrained machines should skip this tactic; a tmpfs that swaps to disk is slower than a decent NVMe drive, and an out-of-space tmpfs mid-Glibc is a special kind of misery.

Compiler Flags: The Line You Should Not Cross

Here is where the speedrun gets dangerous. It is tempting to export aggressive CFLAGS — -O3, -march=native, -flto — and watch everything compile faster and run faster. Resist that temptation for the core toolchain. The book's build instructions are tested against specific optimization levels, and deviating from them is the fastest route to a system that compiles cleanly and then fails in ways that are miserable to diagnose: segfaults in Glibc internals, miscompiled math libraries, a toolchain that builds itself fine but produces broken binaries.

The safe middle ground: -march=native and moderate optimization are generally tolerable for later packages once the toolchain is self-hosted and stable, and many builders apply targeted flags via /etc/profile or package-specific overrides after the base system is complete. But treat chapter five and six as sacred ground. The minutes you save are not worth the days you may spend debugging.

Caching: Build Twice, Compile Once

Compiler caching via ccache is the quiet hero of repeat builds. If you are the kind of builder who tears down and rebuilds regularly — and most of us are — a warm ccache can reduce a second full build to a fraction of the first. It requires no changes to the book's instructions beyond prepending the ccache path to your PATH, and it introduces no correctness risk, since a cache hit is by definition a byte-identical compilation result.

The tradeoff is disk space and a first-build penalty while the cache fills. For anyone maintaining multiple LFS machines or experimenting with package versions, it pays for itself immediately.

The Final Ledger

The complete timed run, with parallel make, tmpfs, sane flags, and ccache warm from a prior toolchain build, came to 41 hours — down from an estimated 70-plus for my first unhurried attempt. Nothing was skipped, no tests were waived beyond what the book itself recommends, and the resulting system passed its own sanity checks and booted without complaint.

Speed, it turns out, is not about cutting corners. It is about knowing exactly where the corners are, and choosing deliberately which ones to round.

All Articles

Related Articles

Your System, Your Rules: Wiring a Real Package Manager Into a Hand-Built LFS Base

Your System, Your Rules: Wiring a Real Package Manager Into a Hand-Built LFS Base

Nothing Proprietary, Nothing Missing: Assembling a Zero-Cost LFS Toolchain

Nothing Proprietary, Nothing Missing: Assembling a Zero-Cost LFS Toolchain

Three Packages, One Foundation: Understanding the Glibc-GCC-Binutils Triad Before Your Next Build

Three Packages, One Foundation: Understanding the Glibc-GCC-Binutils Triad Before Your Next Build