A Linux Distro from Scratch
I think like many people, I use Linux on a daily basis. Personally, I use NixOS. But ultimately, I don’t really know how everything works under the hood.
I’ve stumbled upon videos by an American, Nir Lichtman I believe, who has fun creating different Linux distributions from scratch for various use cases.
Here is an example video
The concept is really cool and it makes the whole thing very accessible, so thanks to him for these videos.
IMPORTANTThe final distribution is not intended for production use, but just for educational purposes.
So in this post, I’m going to show you what I did to create a minimal Linux distribution from scratch.
Technical Prerequisites
In my case, I’ll be doing this on an Ubuntu Docker container. And to run the VM, I use QEMU, which you can easily install.
On NixOS, I simply ran: nix-shell -p qemu
For other distributions, you can check the official QEMU documentation. The docker documentation. And by the way, the official Linux From Scratch documentation which is very well made.
Division
We will need three blocks to make our distribution work:
kernel(the Linux kernel)User Space(BusyBox)bootloader(Syslinux)
The Linux Kernel
As I told you at the beginning, I’m going to use an Ubuntu Docker container to do all of this. So my first step is to launch it.
docker run —privileged -it ubuntu

We find ourselves in the container’s shell, the first step is to update and install the necessary dependencies.
apt update && apt upgrade -y apt install bzip2 git vim make gcc libncurses-dev flex bison bc cpio libelf-dev libssl-dev -y

We have our dependencies installed, now we can clone the Linux kernel repository.
NOTEI prefer to clone it in the user’s directory but that’s up to you (/home/ubuntu).
git clone —depth 1 https://github.com/torvalds/linux.git
*—depth 1 - to avoid getting the full git history of the kernel, just the last commit

Since we are going for an x86_64 installation, we will configure the kernel for this architecture. We use a configuration already made by the kernel developers.
make x86_64_defconfig

Once done, we can start compiling the kernel. We can either use make or make -j N where N is the number of cores of your processor to speed up compilation.
I will use all available cores to go faster.
make -j $(nproc)


Kernel: arch/x86/boot/bzImage is ready (#1)
Once compilation is finished, we can see the kernel image in the arch/x86/boot/bzImage directory.
We create a boot-files directory at the root and copy the kernel image into it.
mkdir /boot-files mv arch/x86/boot/bzImage /boot-files
Done for the kernel, let’s move on to the user space.
User Space - BusyBox
The official BusyBox documentation.
We return to the user space /home/ubuntu and clone the busybox repository.
cd /home/ubuntu git clone —depth 1 https://git.busybox.net/busybox cd busybox
It’s the same principle as for the kernel: we configure and compile.
To initialize the default configuration, we use make defconfig.
make defconfig
We want the busybox version in static mode so that everything is in a single binary. So we modify the configuration in settings; we need to uncheck the Build static binary (no shared libs) section. To do this, run the command:
make menuconfig
Once in the menu, go to Settings (enter).

Once toggled, it’s not over because with recent Ubuntu versions we must disable tc. To do this, go to Networking Utilities and uncheck tc (8.3 kb) (it might be another value Xkb).

Once done, do exit and save the configuration. We then build busybox the same way as the kernel.
make -j $(nproc)

In the directory we created previously /boot-files, we create an initramfs directory and copy the busybox binary into it (mkdir /boot-files/initramfs).
We install busybox in this directory using the make install command with the CONFIG_PREFIX variable pointing to the installation directory.
make CONFIG_PREFIX=/boot-files/initramfs install

Our system doesn’t have a shell yet, we will create an init script which will be the first process launched by the kernel. What’s funny about the init file is that we tell it to use itself as a shell.
NOTEMake sure to create the
initfile in the/boot-files/initramfsdirectory and give it execution permissions with the commandchmod +x init.
Before that, we create the important folders in the initramfs directory.
mkdir /boot-files/initramfs/{proc,sys,dev}
#!/bin/sh
mount -t proc proc /procmount -t sysfs sysfs /sysmount -t devtmpfs devtmpfs /dev
echo "Welcome to my distribution"
exec /bin/shStill in the /boot-files/initramfs directory, we create a compressed cpio archive which will be used by the kernel at startup.
cd /boot-files/initramfs find . | cpio -o -H newc > ../init.cpio
That’s it for the user space, let’s move on to the bootloader.
Bootloader - Syslinux
Here we will use Syslinux as the bootloader, the official doc is here.
We install it because it’s not installed by default.
cd /boot-files # going back to the boot-files directory apt install syslinux -y
So once installed, we will create a 50MB file that will serve as a virtual hard drive for our distribution.
dd if=/dev/zero of=boot bs=1M count=50

We need dosfstools to format the file to FAT32.
apt install dosfstools -y mkfs -t fat boot syslinux boot
Here we mount the virtual hard drive, format it, and install syslinux on it.
Voila, we now have a boot file containing our minimal Linux distribution. The goal is to run it on a virtual machine.
Since we are on a Docker container, we must transfer the boot file to the host. To do this, we use the docker cp command.
In another terminal on the host:
docker cp <container_id>:/boot-files/boot .

Now we must launch the virtual machine with QEMU using the boot file as the hard drive.
qemu-system-x86_64 boot

But we have an issue, it tells us No configuration file found. No problem, we will manually give it the configuration file we created previously.
So in boot we write:
WARNINGThe keyboard is QWERTY
/bzImage -initrd=/init.cpio
Press enter and there we go, we have our minimal Linux distribution.

NOTEWe can clearly see our custom message “Welcome to my distribution”.
You can now play with your minimal Linux distribution. You can type commands like ls, cd, pwd, etc.
That’s roughly how it works. Of course, there are other bootloaders, other user spaces, and plenty of other things to add to make the distribution more complete, but for a start, it’s not bad.
Thanks to Nir Lichtman for the inspiration.
Thanks for reading! If you have any questions, don’t hesitate to ask me on X.