Building Linux From Scratch on a Google Cloud Virtual Machine

Part 1: The Build Environment

Michael Fitzgerald
The Startup
8 min readAug 12, 2020

--

In Part 1 of this series, we will set up a build environment to build Linux From Scratch.

Links to Part 2, Part 3, Part 4

I decided to build Linux from scratch as a learning project to gain a better understanding of Linux internals. I spend a lot of time developing in the cloud, and cloud technology seems to have Linux at its heart, so it seemed worthwhile to give this a shot. Besides, I have major graybeard yearnings.

Note: we will be following LFS Version 9.1-systemd. I will use “LFS x.x.x…” when referring to specific parts. Many of the commands are copied verbatim.

Some of my specific learning goals:

  • source compilation and debugging — I’m C-illiterate
  • increased use of binutils and coreutils
  • low-level kernel features like namespaces — what make containers tick

Building a new Linux system is done most easily within an existing Linux system. The easiest way to get up and running with a Linux system is to fire up a virtual machine on one of the many cloud services. I use Google Cloud Platform at my job, so we will use it here since I’m familiar with it.

Linux within linux.

Start a GCP VM Instance

1. Sign up for the free tier of Google Cloud Platform and create a new project. Google’s documentation is excellent, so if you’re stuck somewhere, just google it.

I will use gcloud commands in this series for consistency because sharing text commands is clearer than point and click descriptions. Anything done with these commands can also be done with the Cloud Console GUI.

2. Install the gcloud SDK. It is a command line utility that makes interacting with Google Cloud facilities easy. Follow the documentation to authenticate and set the configuration.

Here’s the output gcloud config list for my configuration:

3. Create a VM with the following command. Replace the instance name, zone, and disk name accordingly.

This command creates a Google Cloud VM

That command did a number of important things:

  • Provisioned a 1-cpu, 3.75GB VM running Debian Linux. We can (and will) increase the compute resources when we start compiling source code.
  • Enabled a serial port that will let us interact with the VM during boot time. This will be critical when we want to switch between Linux kernel installations. You can read more about serial ports and consoles here.
  • Created and attached a secondary persistent disk. We will need to do some disk and filesystem manipulation throughout the course of the build, and it doesn’t seem possible to partition the primary boot disk without borking the VM. Using a secondary disk is a way around this. (GCP sets 200GB as the default minimum for performance reasons)

4. Connect to the VM using gcloud.

Use this command to shell into the Linux os:

gcloud compute ssh [INSTANCE-NAME]

This gcloud feature is very convenient. It saves us from having to manually configure ssh and transfer keys.

Set up a user password. It could save us a headache down the road.

sudo passwd $(whoami)

Update the package repos and upgrade packages.

sudo apt-get update

In another terminal, use this command to open a serial console:

gcloud compute connect-to-serial-port [INSTANCE-NAME]

Notice the output of the serial console is different from the shell. It displays hardware and system-level diagnostic information. We will use it primarily to interact with the bootloader and to get ourselves out of any goofs should we make them.

The default system configuration has the bootloader timeout set to 0s, which means we can’t interact with it. Let’s change that and then reboot so we can see the bootloader menu in the serial console.

In the shell session, edit /etc/default/grub.d/15_timeout.cfg. Set the timeout to 30s. Update GRUB(the bootloader) with sudo update-grub and then reboot the VM with sudo reboot .

Monitor the serial console. You should see something like this:

That’s the GRUB bootloader menu, and access to it will be key in later steps.

Feel free to keep the serial console open, but reconnect a shell session for the next part.

Format and Partition the Attached Disk

EDIT (8–27–20): After progressing through the final stages of building, it became clear that the boot and home partitions are not needed. We will use the boot partition on the primary disk (sda). A primary ‘root’ partition on sdb is still needed.

The secondary disk we attached needs to formatted, partitioned, and mounted before we can do anything with it.

  1. Find your secondary disk with the lsblk command.

You should see a tree structure with the primary disk, sda, and its partitions, and the secondary disk, sdb, which has no partitions.

2. The disk needs to be labeled before it is recognized by the system and bootloader. We will use the parted utility to label sdb as gpt (GUID Partition Table)

You might see an informational warning about updating /etc/fstab. We will address that later.

We need to partition sdb before we start working on it. LFS 2.4 recommends a main or ‘root’ partition and two additional partitions: boot and home. Our disk is 200GB, so will assign 4GB to boot, 96 GB to home, and 100GB to main.

3. Create the partitions.

The output of lsblk should look like this:

Sweet, sweet partitions.

4. Make file systems on the partitions.

5. Set the LFS environment variable.

This points to the mount point for the LFS installation. We will need to set it for both user and root on the build system.

6. Mount the partitions.

The output of lsblk should look like this:

I’m working from a different computer, hence the minor differences in terminal appearance.

7. Update /etc/fstab so the system mounts the partitions automatically at boot

Prepare the host system requirements (build tools).

  1. Run the script provided in LFS 2.2 to check if all the needed build tools are installed.

Your output should be similar to this:

Note that some packages are not found. We will have to install them. For those packages that are installed, a version is listed. We need to ensure that the version meets or exceeds those specified in LFS 2.2. Since we are working on an up-to-date Debian system, the versions all exceed the recommendations.

The missing packages can be installed with:

2. Download the source files needed to build Linux from scratch.

LFS 3.1 provides a manifest of source file download URIs that can be used with wget. You might need to install it first: sudo apt-get install wget.

Create the source directory for the files and modify its file permissions.

Obtain the package manifest ‘wget-list’.

The file ‘wget-list’ should be in your home directory. Now download the source files to the source directory.

To check that everything was downloaded, we can make a list of all the packages in the wget-list and compare it to the contents of the sources directory. One of my downloads timed out on the first pass and was missing.

3. Make the tools directory and symlink it to the host system.

The reason for the symlink as directly described in LFS 4.2:

The created symlink enables the toolchain to be compiled so that it always refers to /tools, meaning that the compiler, assembler, and linker will work both in Chapter 5 (when we are still using some tools from the host) and in the next (when we are “chrooted” to the LFS partition).

4. Set up the lfs user.

5. Continue the environment set up as described in LFS 4.4

This completes Part 1.

--

--