Background

Advantage

  • Ultra fast after boot
  • No need to consider about power failure
  • Great for experiment, you’ll get a fresh OS after reboot
  • File based maintenance, easy to copy and paste with cross platform

Concern

  • Need lots of RAM to boot (<100MB v.s. 1.5GB)
  • There is no error correction if you’re using a consumer RAM

Requirement

  • A physical or virtual machine
  • At least 1G disk to store boot files
  • At least 2G RAM to store rootfs, above 4G is recommend

Enviroment

Install Debian in VirtualBox with separate /boot partition

So that allow us to umount the /boot and mount specific disk which we want to build as boot medium.

Config grub and linux command line

This will allow us to access the GRUB and CLI via serial console

sudo dpkg-reconfigure grub-pc
 - Linux command line: console=tty0 console=ttyS0,115200n8
 - Linux default command line: <empty>
 - GRUB install devices: <no change>
sudo nano /etc/default/grub

Add following lines

GRUB_TERMINAL="console serial"
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"
GRUB_DISABLE_OS_PROBER=true

Update GRUB

sudo grub-mkconfig -o /boot/grub/grub.cfg

Install additional software

sudo apt update && sudo apt install htop screen wget tcpdump dosfstools exfat-utils mdadm lvm2 linux-cpupower ethtool -y
sudo apt clean

Customize initramfs script for RAM based rootfs

Make a backup, add another file.

cd /usr/share/initramfs-tools/scripts/
sudo cp local local.original
sudo cp local local.ramfs
sudo nano local.ramfs

Find local_mount_root(), comment out the normal root mount command and add following lines.

# if ! mount ${roflag} ${FSTYPE:+-t "${FSTYPE}"} ${ROOTFLAGS} "${ROOT}" ">
#         panic "Failed to mount ${ROOT} as root file system."
# fi
mkdir /mount
mount -t ${FSTYPE} ${ROOT} /mount
mount -t tmpfs -o size=95% none ${rootmnt}
cd ${rootmnt}
/mount/init.sh

List the FAT and exFAT kernel module

sudo cp /etc/initramfs-tools/modules /etc/initramfs-tools/modules.original
sudo cp /etc/initramfs-tools/modules /etc/initramfs-tools/modules.ramfs
ls /lib/modules/`uname -r`/kernel/fs/fat/ | cut -f1 -d '.' | sudo tee -a /etc/initramfs-tools/modules.ramfs
ls /lib/modules/`uname -r`/kernel/fs/exfat/ | cut -f1 -d '.' | sudo tee -a /etc/initramfs-tools/modules.ramfs
ls /lib/modules/`uname -r`/kernel/fs/nls/ | cut -f1 -d '.' | sudo tee -a /etc/initramfs-tools/modules.ramfs

Customize fstab for RAM based rootfs

Make a backup, add another file. Set the tmpfs as / .

sudo cp /etc/fstab /etc/fstab.original
echo 'none /   tmpfs size=95% 0 0' | sudo tee -a /etc/fstab.ramfs

Startup script

Our customize initramfs will copy start.sh from boot medium to / , systemd will handle it after whole system boot up. This allow user to add additional stuff by editing just start.sh after archive the rootfs.tar.gz

sudo nano /etc/systemd/system/startup.service
[Unit]
Description=Startup script
After=network.target

[Service]
ExecStart=/start.sh

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable startup.service

Pack the rootfs

Make a ramdisk

sudo mount -t tmpfs -o size=2G tmpfs /tmp

Use all customize files

Copy the customize files to default filename

sudo cp /usr/share/initramfs-tools/scripts/local.ramfs /usr/share/initramfs-tools/scripts/local
sudo cp /etc/initramfs-tools/modules.ramfs /etc/initramfs-tools/modules
sudo cp /etc/fstab.ramfs /etc/fstab

Always print the sudo lecture

echo 'Defaults lecture = always' | sudo tee -a /etc/sudoers.d/privacy

Archive the rootfs

sudo tar zcvf /tmp/rootfs.tar.gz --one-file-system /

Copy the archive to home directory

mkdir -p /bootfiles/
cp /tmp/rootfs.tar.gz /bootfiles/.

Prepare the boot files

Build the customize initramfs

sudo mkinitramfs -o /bootfiles/initrd.img-`uname -r`

Check if fat kernel module inclued

lsinitramfs /bootfiles/initrd.img-`uname -r` | grep fat

Copy the kernel

sudo cp /boot/vmlinuz-`uname -r` /bootfiles/vmlinuz-`uname -r`

Make a start up script for initramfs

nano /bootfiles/init.sh
#!/bin/sh

echo "Copying start.sh ..."
cp /mount/start.sh .
echo "Copying rootfs.tar.gz ..."
cp /mount/rootfs.tar.gz .
umount /mount
echo "Extracting from rootfs.tar.gz ..."
tar zxvf rootfs.tar.gz
rm rootfs.tar.gz

Make a start up script for systemd startup.service

nano /bootfiles/start.sh
#!/bin/bash

## Network ##
sed -in '/enp/d' /etc/network/interfaces

for i in $(ip link show | grep enp | cut -f2 -d' ' | sed 's/://g'); do
	echo "" >> /etc/network/interfaces
	echo "auto ${i}" >> /etc/network/interfaces
	echo "allow-hotplug ${i}" >> /etc/network/interfaces
	echo "iface ${i} inet dhcp" >> /etc/network/interfaces
	if ! ethtool ${i} | grep -sq 'Supports Wake-on: d'; then
		echo "up ethtool -s ${i} wol g" >> /etc/network/interfaces
	fi
done

systemctl restart networking.service

Switch back to original files

Now the boot files are ready, so we are going to recover modified files.

sudo cp /usr/share/initramfs-tools/scripts/local.original /usr/share/initramfs-tools/scripts/local
sudo cp /etc/initramfs-tools/modules.original /etc/initramfs-tools/modules
sudo cp /etc/fstab.original /etc/fstab
sudo systemctl disable startup.service
sudo rm /etc/systemd/system/startup.service
sudo systemctl daemon-reload

Complicated? Use the shell script version!

Create boot medium

Insert your USB thumb drive to VirtualBox, mkfs if necessary.

sudo fdisk /dev/sdb
sudo mkfs.exfat /dev/sdb1

Mount the disk to /boot

sudo umount /boot
sudo mount /dev/sdb1 /boot

Copy boot files

sudo cp /bootfiles/* /boot

Install GRUB

This will install the grub bootloader and mark the disk bootable.

sudo grub-install /dev/sdb

update-grub will generate the boot menu (grub.cfg) by detecting the content in /boot

sudo update-grub

Check the UUID of your thumb drive

sudo blkid

Modify the root UUID in grub.cfg

sudo nano /boot/grub/grub.cfg

Unmount and poweroff the virtual machine

sudo umount /boot
sudo poweroff

Your disk is ready to boot!!

Reference