Dan Callaghan

Fedora from scratch

I once struggled through a Linux From Scratch installation, many years ago when I first became seriously interested in Linux. I spent days, even weeks, trying to get everything compiled and working. I don’t remember if I ever fully succeeded, although I certainly learnt a lot — even if most of what I learnt was pointless arcana that is mostly obsolete now.

Eventually I decided it should be possible to automate all these repetitive steps for building packages, which led me to Gentoo. For several years I was a happy user of Gentoo, then later Exherbo. Neither Gentoo nor Exherbo have an installer, leaving the user to create filesystems and bootstrap the installation themselves by hand. So I’ve had plenty of practice at installing Linux distros the hard way.

These days the appeal of tweaking every package’s build options and compiling my own kernel has dwindled, and I run Fedora instead. Anaconda, the Fedora installer, is far from perfect; but given the outrageous number of possible configurations and edge cases it is expected to deal with, it does a remarkable job. And in recent releases it has been improving too, with a new UI and more flexible storage configuration.

Nevertheless, when I got my new work laptop I decided to try installing Fedora without using Anaconda, as an experiment. Let’s call it “Fedora From Scratch”.

Start by booting a Fedora live CD or live DVD of your choice. I used a Fedora 20 Beta XFCE Live DVD, dd’ed to a USB flash drive. With EFI you can just plug it in and pick it from the boot menu. You could also boot Anaconda over the network and drop to a shell using Ctrl+Alt+F2. Anything that has filesystem utilities and yum should work.

The first step is to partition your disks and create filesystems.

In the past I have built towering monstrosities of LVM upon LUKS upon mdraid upon GPT. There is a setup like that running in my workstation, with four hard disks and one SSD, all of varying sizes, speeds, and ages. But this is just a little laptop with a single solid-state disk, so I opted for a relatively simple configuration. The partition table is GPT (necessary for EFI booting). I created a 512MB “EFI System Partition”, and in the rest of the space I created a LUKS volume with an XFS volume on top.

If you’re following along, this is where you set up whatever crazy disk configuration your heart desires. You’re limited only by your imagination! …and by what Dracut knows how to handle.

Once everything is set up, mount it all in a directory which will become your installed system. Here I used /mnt/local.

gdisk /dev/sda
cryptsetup luksFormat /dev/sda2
cryptsetup luksOpen /dev/sda2 luks-1b5a6440-34ff-4469-b625-45d9ad9dc6ae
mkfs.xfs -L dill-fedora /dev/mapper/luks-1b5a6440-34ff-4469-b625-45d9ad9dc6ae
mkfs.vfat -n EFI /dev/sda1
mkdir /mnt/local
mount LABEL=dill-fedora /mnt/local
mkdir /mnt/local/boot
mount LABEL=EFI /mnt/local/boot

One important gotcha, which I discovered the hard way: when you open the LUKS volume now, you must use the same name you will use later in the installed system. The reason is that Dracut uses the currently mapped name for the LUKS volume when it generates the initrams. Here I used luks-$UUID which is the convention used by Anaconda.

Next, use yum to populate your new filesystem. dnf works too.

yum --releasever=20 --installroot=/mnt/local groupinstall core

Now you can enter the chroot to finish configuring things.

cp /etc/resolv.conf /mnt/local/etc/
mount -t sysfs none /mnt/local/sys
mount -t proc none /mnt/local/proc
mount -t efivarfs none /mnt/local/sys/firmware/efi/efivars
mount -o bind /dev /mnt/local/dev
chroot /mnt/local /bin/bash

The following steps are all done from the chroot’ed shell. Configure your filesystems, hostname, and root password.

cat >/etc/crypttab <<EOF
luks-1b5a6440-34ff-4469-b625-45d9ad9dc6ae UUID=1b5a6440-34ff-4469-b625-45d9ad9dc6ae

cat >/etc/fstab <<EOF
LABEL=EFI /boot auto defaults 0 2
LABEL=dill-fedora / auto defaults,noatime,nodiratime 0 1

echo dill.djc.id.au >/etc/hostname


With EFI firmware, a “boot loader” in the traditional sense is not needed. You could boot your kernel directly if you wanted. Gummiboot is a “boot manager”, which means you can boot it and it will show a menu allowing you to pick a kernel or other operating system. I highly recommend it because it’s very simple and just works.

yum install efibootmgr gummiboot
gummiboot install

Lastly, install a kernel and any other packages necessary for your storage configuration. Here I am installing cryptsetup for the LUKS volume and xfsprogs for the XFS volume.

It’s important that these packages are installed before the kernel (or in the same RPM transaction). The kernel package has a %posttrans scriptlet which uses Dracut to build an initramfs image, and if Dracut cannot find the programs it needs for mounting and checking your filesystems (in this case, cryptsetup and fsck.xfs) then they will be silently skipped from the initramfs, leaving you with an unbootable system.

yum install kernel cryptsetup xfsprogs

After the kernel has been installed, you will find a boot loader configuration has been written to /boot/loader/entries (according to the new systemd boot loader spec). The file will be named after the automatically generated machine ID for your new installation.

vi /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.11.10-301.fc20.x86_64.conf

The kernel options in this file are copied from the currently running kernel, so there will be some irrelevant options from the live DVD such as BOOT_IMAGE=. Delete all that, and add the two options you need to boot your newly installed system: ro and root=. If you like Plymouth, you can also add quiet rhgb to get a boot animation. If you have trouble getting the system to boot, you can add rd.debug rd.shell instead to debug problems in the initramfs.

options ro root=LABEL=dill-fedora

I also noticed that some SELinux labels are not set correctly in the installed system. I’m not sure of the reason for this, maybe the SELinux policy doesn’t take effect while chroot’ed? So you will need to trigger a relabel on reboot.

touch /.autorelabel

Now you’re ready to reboot. If all goes well you should have a working minimal Fedora installation, occuping less than 700MB of disk space, and a getty login prompt. Log in, set up the clock, and start installing your favourite packages.

yum install chrony
timedatectl set-timezone Australia/Brisbane
yum install vim bash-completion ...

Overall I was surprised at how easy this was. Yum does all the hard work with its --installroot option. Almost everything just worked. By far the most painful part was getting Dracut to generate a working initramfs. The problem is exacerbated by how difficult and time-consuming it can be to debug problems in early boot. The two most important gotchas I found with Dracut are described above: it’s sensitive to the state of the system at the time the initramfs is generated, and if some programs are missing it silently omits functionality from the initramfs image.

Going through this process by hand made me realise that Anaconda is not much more than a complicated storage configuration utility, plus a rudimentary yum frontend. In future I will probably do all my Fedora installations this way, since I find it easier to just set up the filesystems by hand than to tell Anaconda how I want it done.