Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save disjustin/b68177603fafc91e1244c2aef46ab73c to your computer and use it in GitHub Desktop.
Save disjustin/b68177603fafc91e1244c2aef46ab73c to your computer and use it in GitHub Desktop.
Instructions how to install Debian using debootstrap

Instructions how to install Ubuntu using debootstrap. Below instructions were verified to work with debootstrapping Ubuntu 24.04.2.

Table of contents

Conventions

  • /dev/PARTITION: replace it with the partition where Ubuntu is to be installed e.g. /dev/vda2
  • /mnt: mountpoint for /dev/PARTITION, you can change it to something else
  • /dev/GRUBDISK: the disk on which you want grub to be installed e.g. /dev/sda (don't confuse it with a partition e.g. /dev/sda1)

Essential steps

Install debootstrap

Prepare work directory, e.g:

cd /tmp

Go https://deb.ubuntu.org/ubuntu/pool/main/d/debootstrap/?C=M;O=D and download latest debootstrap_X.X.X_all.deb, e.g.:

wget 'https://deb.ubuntu.org/ubuntu/pool/main/d/debootstrap/debootstrap_1.0.124_all.deb'

Then install it:

dpkg -i debootstrap_*.*.*_all.deb

Prepare disk for installing boot loader

BIOS systems require different steps than UEFI systems.

BIOS systems

If you intend to boot the system you will need a boot loader and it needs a place on disk (see step Install boot loader to disk).

First check what is the partition table using either fdisk or parted:

  • fdisk /dev/GRUBDISK -l and look for Disklabel type:.
    • Disklabel type: gpt means GPT.
    • Disklabel type: dos means MBR.
    • no Disklabel type: probably means partition table is missing.
  • parted /dev/GRUBDISK print and look for Partition Table:
    • Partition Table: gpt means GPT.
    • Partition Table: msdos means MBR.
    • Partition Table: unknown probably means partition table is missing.

GPT specific instructions

GRUB needs unformatted BIOS boot partition (see: https://wiki.archlinux.org/title/GRUB#GUID_Partition_Table_(GPT)_specific_instructions). This partition should be big enough, but around 1000 KiB should suffice. To make one you can use e.g. gdisk:

apt install -y gdisk
gdisk /dev/GRUBDISK

In gdisk, to make some partition a BIOS boot partition, change its type to ef02.

Below is an example of how one can partition the whole disk e.g. /dev/vda (has Logical block size = 512 bytes; first created partition is BIOS boot partition, second is a partition for the new Ubuntu):

gdisk /dev/vdb
o
y
n


p
w
y
root@justin-wong-KVM:~# gdisk /dev/vdb
GPT fdisk (gdisk) version 1.0.10

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): o
This option deletes all partitions and creates a new protective MBR.
Proceed? (Y/N): y

Command (? for help): x

Expert command (? for help): l
Enter the sector alignment value (1-65536, default = 2048): 1

Expert command (? for help): m

Command (? for help): n
Partition number (1-128, default 1):
First sector (34-83886046, default = 34) or {+-}size{KMGTP}:
Last sector (34-83886046, default = 83886046) or {+-}size{KMGTP}:
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'

Command (? for help): p
Disk /dev/vdb: 83886080 sectors, 40.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 39AE6518-0741-4742-B7B3-270423900F83
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 83886046
Partitions will be aligned on 1-sector boundaries
Total free space is 0 sectors (0 bytes)

Number  Start (sector)    End (sector)  Size       Code  Name
   1              34        83886046   40.0 GiB    8300  Linux filesystem

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/vdb.
The operation has completed successfully.
root@justin-wong-KVM:~# gdisk /dev/vdb -l
GPT fdisk (gdisk) version 1.0.10

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/vdb: 83886080 sectors, 40.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 39AE6518-0741-4742-B7B3-270423900F83
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 83886046
Partitions will be aligned on 2-sector boundaries
Total free space is 0 sectors (0 bytes)

Number  Start (sector)    End (sector)  Size       Code  Name
   1              34        83886046   40.0 GiB    8300  Linux filesystem
root@justin-wong-KVM:~#

MBR specific instructions

The post-MBR gap (between MBR region and the start of the first partition) should be enough for GRUB, so you don't have to do anything.

If your first partition starts at 1 MiB (you can check it using fdisk /dev/GRUBDISK -l or parted /dev/GRUBDISK print; one sector = logical sector size), then all should be fine. Otherwise you may need to make the beginning of the first partition have bigger offset.

UEFI systems

TODO

For more information check out: https://wiki.ubuntu.org/UEFI and https://wiki.archlinux.org/title/GRUB#UEFI_systems (should be more helpful).


For more information about setting up GRUB see: https://wiki.archlinux.org/title/GRUB

Set up filesystem for Ubuntu

E.g.

mkfs.ext4 /dev/vdb

Mount filesystem

mount /dev/vdb /mnt

Install base system

Usage: debootstrap --arch ARCH RELEASE DIR MIRROR E.g.

debootstrap --arch amd64 stable /mnt https://deb.ubuntu.org/ubuntu
debootstrap --arch=amd64 --variant=minbase noble /mnt http://us.archive.ubuntu.com/ubuntu/

Chroot into installed base system

mount --make-rslave --rbind /proc /mnt/proc
mount --make-rslave --rbind /sys /mnt/sys
mount --make-rslave --rbind /dev /mnt/dev
mount --make-rslave --rbind /run /mnt/run
chroot /mnt /bin/bash

Set up editor

  • nano: should already be there as default
  • vim:
apt install -y vim nano
# update-alternatives --config editor

Configure apt sources

Fill /etc/apt/sources.list:

apt install -y lsb-release
# export CODENAME=noble
# CODENAME=$(lsb_release --codename --short)
cat > /etc/apt/sources.list << EOF
deb http://archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse

deb http://archive.ubuntu.com/ubuntu/ noble-updates main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ noble-updates main restricted universe multiverse

deb http://archive.ubuntu.com/ubuntu/ noble-security main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ noble-security main restricted universe multiverse
EOF

Finally, update the package indexes:

apt update

For more details see: https://wiki.ubuntu.org/SourcesList

Install systemd

apt install -y libterm-readline-gnu-perl systemd-sysv

systemd is a system and service manager for Linux. It provides aggressive parallelization capabilities, uses socket and D-Bus activation for starting services, offers on-demand starting of daemons, keeps track of processes using Linux control groups, maintains mount and automount points and implements an elaborate transactional dependency-based service control logic.

Null the fstab file

cat /dev/null > /etc/fstab

Machine-id and divert

dbus-uuidgen > /etc/machine-id
ln -fs /etc/machine-id /var/lib/dbus/machine-id

The /etc/machine-id file contains the unique machine ID of the local system that is set during installation or boot. The machine ID is a single newline-terminated, hexadecimal, 32-character, lowercase ID. When decoded from hexadecimal, this corresponds to a 16-byte/128-bit value. This ID may not be all zeros.

dpkg-divert --local --rename --add /sbin/initctl
ln -s /bin/true /sbin/initctl

dpkg-divert is the utility used to set up and update the list of diversions.

Upgrade packages

apt -y upgrade

Install Live System packages

Install packages needed for Live System

apt install -y \
sudo \
ubuntu-standard \
casper \
discover \
laptop-detect \
os-prober \
network-manager \
net-tools \
wireless-tools \
wpagui \
locales \
grub-common \
grub-gfxpayload-lists \
grub-pc \
grub-pc-bin \
grub2-common \
grub-efi-amd64-signed \
shim-signed \
mtools \
binutils
apt install -y --no-install-recommends linux-generic

Choose timezone

# dpkg-reconfigure tzdata
timedatectl set-timezone America/Los_Angeles

Configure locales

apt install -y locales
#dpkg-reconfigure locales
sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' /etc/locale.gen
echo 'LANG=en_US.UTF-8' > /etc/default/locale
locale-gen

E.g. select en_US.UTF-8, then C.UTF-8.

Install new kernel

Install the kernel image, e.g.:

apt install -y linux-firmware
apt install -y linux-image-`uname -r`
apt install -y linux-headers-`uname -r`
apt install -y linux-modules-extra-`uname -r`

Remove old kernel

# list all installed kernels:
dpkg --list | egrep -i --color 'linux-image|linux-headers|linux-modules' | awk '{ print $2 }'
# remove the old kernel packages
apt purge linux-*-6.8.0-58*

Remove unused packages

apt autoremove -y

Set a custom hostname

Set hostname e.g.:

echo "ubuntu-fs-live" > /etc/hostname

where MY_HOSTNAME is the custom hostname you want to set.

Then update /etc/hosts:

cat > /etc/hosts << EOF
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
EOF

Set root's password

(echo 'supermicro'; echo 'supermicro') | passwd

Install ssh-server and enable root login

# enable root login over ssh
apt install -y openssh-server
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

Install additional software

Install the equivalent of "Development Tools"

apt install -y build-essential less tmux

Enable os_prober in grub

Install the GRUB2 boot loader

apt install -y grub2

This will make grub search for and add to menu other systems like Windows or other Linux distribution.

(cat /etc/default/grub; echo GRUB_DISABLE_OS_PROBER=false) | sudo tee /etc/default/grub && sudo update-grub

Finish installation

Create the image directory and populate it

We are now back in our build environment after setting up our live system and will continue creating files necessary to generate the ISO.

  1. Create directories
mkdir -p /image/{casper,isolinux,install}
  1. Copy kernel images
cp /boot/vmlinuz-**-**-generic /image/casper/vmlinuz
cp /boot/initrd.img-**-**-generic /image/casper/initrd
  1. Copy memtest86+ binary (BIOS and UEFI)
apt install -y unzip
wget --progress=dot https://memtest.org/download/v7.00/mt86plus_7.00.binaries.zip -O /image/install/memtest86.zip
unzip -p /image/install/memtest86.zip memtest64.bin > /image/install/memtest86+.bin
unzip -p /image/install/memtest86.zip memtest64.efi > /image/install/memtest86+.efi
rm -f /image/install/memtest86.zip

GRUB menu configuration

  1. Create base point access file for grub
touch /image/ubuntu
  1. Create image/isolinux/grub.cfg
cat > /image/isolinux/grub.cfg << EOF

search --set=root --file /ubuntu

insmod all_video

set default="0"
set timeout=5

menuentry "Ubuntu FS Live" {
   linux /casper/vmlinuz boot=casper nopersistent toram quiet splash ---
   initrd /casper/initrd
}

menuentry "Install Ubuntu FS" {
   linux /casper/vmlinuz boot=casper only-ubiquity quiet splash ---
   initrd /casper/initrd
}

menuentry "Check disc for defects" {
   linux /casper/vmlinuz boot=casper integrity-check quiet splash ---
   initrd /casper/initrd
}

grub_platform
if [ "\$grub_platform" = "efi" ]; then
menuentry 'UEFI Firmware Settings' {
   fwsetup
}

menuentry "Test memory Memtest86+ (UEFI)" {
   linux /install/memtest86+.efi
}
else
menuentry "Test memory Memtest86+ (BIOS)" {
   linux16 /install/memtest86+.bin
}
fi
EOF

Create manifest

Next we create a file filesystem.manifest to specify each package and it's version that is installed on the live system. We create another file filesystem.manifest-desktop which specifies which files will be installed on the target system. Once the Ubiquity installer completes, it will remove packages specified in filesystem.manifest that are not listed in filesystem.manifest-desktop.

  1. Generate manifest
dpkg-query -W --showformat='${Package} ${Version}\n' | sudo tee /image/casper/filesystem.manifest
cp -v /image/casper/filesystem.manifest image/casper/filesystem.manifest-desktop
sed -i '/ubiquity/d' /image/casper/filesystem.manifest-desktop
sed -i '/casper/d' /image/casper/filesystem.manifest-desktop
sed -i '/discover/d' /image/casper/filesystem.manifest-desktop
sed -i '/laptop-detect/d' /image/casper/filesystem.manifest-desktop
sed -i '/os-prober/d' /image/casper/filesystem.manifest-desktop

Create diskdefines

README file often found on Linux LiveCD installer discs, such as an Ubuntu Linux installation CD; typically named “README.diskdefines” and may be referenced during installation.

  1. Create file /image/README.diskdefines
cat > /image/README.diskdefines << EOF
#define DISKNAME  Ubuntu from scratch
#define TYPE  binary
#define TYPEbinary  1
#define ARCH  amd64
#define ARCHamd64  1
#define DISKNUM  1
#define DISKNUM1  1
#define TOTALNUM  0
#define TOTALNUM0  1
EOF

Creating image

# access image directory
cd /image
# copy EFI loaders
cp /usr/lib/shim/shimx64.efi.signed.previous isolinux/bootx64.efi
cp /usr/lib/shim/mmx64.efi isolinux/mmx64.efi
cp /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed isolinux/grubx64.efi
  1. Create a FAT16 UEFI boot disk image containing the EFI bootloaders
cd isolinux && \
dd if=/dev/zero of=efiboot.img bs=1M count=10 && \
mkfs.vfat -F 16 efiboot.img && \
LC_CTYPE=C mmd -i efiboot.img efi efi/ubuntu efi/boot && \
LC_CTYPE=C mcopy -i efiboot.img ./bootx64.efi ::efi/boot/bootx64.efi && \
LC_CTYPE=C mcopy -i efiboot.img ./mmx64.efi ::efi/boot/mmx64.efi && \
LC_CTYPE=C mcopy -i efiboot.img ./grubx64.efi ::efi/boot/grubx64.efi && \
LC_CTYPE=C mcopy -i efiboot.img ./grub.cfg ::efi/ubuntu/grub.cfg
  1. Create a grub BIOS image
grub-mkstandalone \
--format=i386-pc \
--output=core.img \
--install-modules="linux16 linux normal iso9660 biosdisk memdisk search tar ls" \
--modules="linux16 linux normal iso9660 biosdisk search" \
--locales="" \
--fonts="" \
"boot/grub/grub.cfg=isolinux/grub.cfg"
  1. Combine a bootable Grub cdboot.img
cat /usr/lib/grub/i386-pc/cdboot.img core.img > bios.img
  1. Generate md5sum.txt
/bin/bash -c "(find . -type f -print0 | xargs -0 md5sum | grep -v -e 'isolinux' > md5sum.txt)"

Cleanup the chroot environment

  1. Each machine should have a unique identifier so be sure to run
truncate -s 0 /etc/machine-id
  1. Remove the diversion
rm /sbin/initctl
dpkg-divert --rename --remove /sbin/initctl
  1. Clean up
apt clean
rm -rf /tmp/* ~/.bash_history
umount /proc
umount /sys
umount /dev/pts
export HISTSIZE=0
exit
umount -R /mnt/*

Compress the chroot

After everything has been installed and preconfigured in the chrooted environment, we need to generate an image of everything that was done by following the next steps in the build environment.

mv /mnt/image .
# create squashfs
mksquashfs /mnt image/casper/filesystem.squashfs \
-noappend -no-duplicates -no-recovery \
-wildcards \
-comp xz -b 1M -Xdict-size 100% \
-e "var/cache/apt/archives/*" \
-e "root/*" \
-e "root/.*" \
-e "tmp/*" \
-e "tmp/.*" \
-e "swapfile"

Squashfs is a highly compressed read-only filesystem for Linux. It uses zlib compression to compress both files, inodes and directories. Inodes in the system are very small and all blocks are packed to minimize data overhead. Block sizes greater than 4K are supported up to a maximum of 64K. Squashfs is intended for general read-only filesystem use, for archival use (i.e. in cases where a .tar.gz file may be used), and in constrained block device/memory systems (e.g. embedded systems) where low overhead is needed.

# write the filesystem.size
printf $(sudo du -sx --block-size=1 /mnt | cut -f1) | sudo tee image/casper/filesystem.size
cd image/
# create iso from the image directory using the command-line
xorriso \
-as mkisofs \
-iso-level 3 \
-full-iso9660-filenames \
-J -J -joliet-long \
-volid "ubuntu-24.04.2-live-server-amd64" \
-output "../ubuntu-live.iso" \
-eltorito-boot isolinux/bios.img \
  -no-emul-boot \
  -boot-load-size 4 \
  -boot-info-table \
  --eltorito-catalog boot.catalog \
  --grub2-boot-info \
  --grub2-mbr /mnt/usr/lib/grub/i386-pc/boot_hybrid.img \
  -partition_offset 16 \
  --mbr-force-bootable \
-eltorito-alt-boot \
  -no-emul-boot \
  -e isolinux/efiboot.img \
  -append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b isolinux/efiboot.img \
  -appended_part_as_gpt \
  -iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 \
  -m "isolinux/efiboot.img" \
  -m "isolinux/bios.img" \
  -e '--interval:appended_partition_2:::' \
-exclude isolinux \
-graft-points \
   "/EFI/boot/bootx64.efi=isolinux/bootx64.efi" \
   "/EFI/boot/mmx64.efi=isolinux/mmx64.efi" \
   "/EFI/boot/grubx64.efi=isolinux/grubx64.efi" \
   "/EFI/ubuntu/grub.cfg=isolinux/grub.cfg" \
   "/isolinux/bios.img=isolinux/bios.img" \
   "/isolinux/efiboot.img=isolinux/efiboot.img" \
   "."
cd ..

Ubuntu live

Procedure for creating Ubuntu live image

Environment setup

The root filesystem will be build on a secondary partitioned virtual drive dev/vdb with 40G allocated storage. I added this disk to the existing ubuntu desktop vm via the cockpit web GUI. Modify the below command template in order to run on the command line of the host server.

Create a virtual disk image

qemu-img create -f qcow2 ubuntu25.04-liveimage.qcow2 40G
qemu-img info ubuntu25.04-liveimage.qcow2

Permanently attach the disk into the domain

virsh dumpxml --domain ubuntu25.04 > domain.xml
nano domain.xml
<disk type='file' device='disk'>
  <driver name='qemu' type='qcow2'/>
  <source file='/var/lib/libvirt/images/ubuntu25.04-liveimage.qcow2'/>
  <target dev='vdb' bus='virtio'/>
</disk>
virsh define domain.xml
virsh domblklist ubuntu25.04
# install prerequisites
apt install -y debootstrap unzip
# setup filesystem for Ubuntu
mkfs.ext4 /dev/vdb
# mount filesystem
mount /dev/vdb /mnt
# install base system
debootstrap --arch=amd64 --variant=minbase plucky /mnt http://us.archive.ubuntu.com/ubuntu/
# chroot into filesystem
mount --bind /dev /mnt/dev
mount --bind /run /mnt/run
chroot /mnt /bin/bash

Chroot

# configure apt sources
cat > /etc/apt/sources.list << EOF
deb http://archive.ubuntu.com/ubuntu/ plucky main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ plucky main restricted universe multiverse

deb http://archive.ubuntu.com/ubuntu/ plucky-updates main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ plucky-updates main restricted universe multiverse

deb http://archive.ubuntu.com/ubuntu/ plucky-security main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ plucky-security main restricted universe multiverse
EOF
# update the package indexes
apt update
# null fstab
cat /dev/null > /etc/fstab
# install systemd
apt install -y libterm-readline-gnu-perl systemd-sysv
# setup uuid, divert for temporary chroot environment
dbus-uuidgen > /etc/machine-id
ln -fs /etc/machine-id /var/lib/dbus/machine-id
dpkg-divert --local --rename --add /sbin/initctl
ln -s /bin/true /sbin/initctl

Packages

apt -y upgrade
# install live system prerequisites
apt install -y \
sudo \
ubuntu-standard \
dracut \
dracut-config-generic \
dracut-live \
discover \
laptop-detect \
os-prober \
network-manager \
net-tools \
wpagui \
locales \
grub-common \
grub-gfxpayload-lists \
grub-pc \
grub-pc-bin \
grub2-common \
grub-efi-amd64-signed \
shim-signed \
mtools \
binutils \
vim \
nano \
lsb-release \
build-essential \
less \
tmux \
grub2 \
unzip \
openssh-server
apt install -y --no-install-recommends linux-generic
# install latest kernel
apt install -y linux-firmware linux-image-`uname -r` linux-headers-`uname -r` linux-modules-extra-`uname -r`
# list all installed kernels
#dpkg --list | egrep -i --color 'linux-image|linux-headers|linux-modules' | awk '{ print $2 }'
# remove the old kernel packages
#apt purge -y linux-*-6.8.0-58*
# remove unused packages
apt autoremove -y

Configuration

# timezone and locale
timedatectl set-timezone America/Los_Angeles
sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' /etc/locale.gen
echo 'LANG=en_US.UTF-8' > /etc/default/locale
locale-gen
# custom hostname
echo "ubuntu-fs-live" > /etc/hostname
'''
cat > /etc/hosts << EOF
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
EOF
'''
# reduce network manager timeout
sed -i 's/nm-online -s -q/nm-online -s -t 10/' /usr/lib/systemd/system/NetworkManager-wait-online.service
# root autologin
cp --force /lib/systemd/system/[email protected] /etc/systemd/system/getty.target.wants/[email protected]
sed -i "s/ExecStart=.*/ExecStart=-\/sbin\/agetty --autologin root --noclear - \$TERM/g" /etc/systemd/system/getty.target.wants/[email protected]
for i in {2..6}; do cp --force /etc/systemd/system/getty.target.wants/[email protected] /etc/systemd/system/getty.target.wants/getty@tty$i.service; done
# root password
(echo 'supermicro'; echo 'supermicro') | passwd
# enable root login over ssh
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
# enable grub os_prober
(cat /etc/default/grub; echo GRUB_DISABLE_OS_PROBER=false) | sudo tee /etc/default/grub && sudo update-grub

ISO files

# copy bootloaders
mkdir -p /image/{casper,isolinux,install}
cp -p /boot/vmlinuz-**-**-generic /image/casper/vmlinuz
cp -p /boot/initrd.img-**-**-generic /image/casper/initrd
# memtest86+ binary (BIOS and UEFI)
wget --progress=dot https://memtest.org/download/v7.00/mt86plus_7.00.binaries.zip -O /image/install/memtest86.zip
unzip -p /image/install/memtest86.zip memtest64.bin > /image/install/memtest86+.bin
unzip -p /image/install/memtest86.zip memtest64.efi > /image/install/memtest86+.efi
rm -f /image/install/memtest86.zip
# base point access file for grub
touch /image/ubuntu
# create grub configuration
cat > /image/isolinux/grub.cfg << EOF

search --set=root --file /ubuntu

insmod all_video

set default="0"
set timeout=5

menuentry "Ubuntu 24.04.2 Live" {
   linux /casper/vmlinuz boot=casper nopersistent toram quiet splash ---
   initrd /casper/initrd
}

menuentry "Install Ubuntu 24.04.2" {
   linux /casper/vmlinuz boot=casper only-ubiquity quiet splash ---
   initrd /casper/initrd
}

menuentry "Check disc for defects" {
   linux /casper/vmlinuz boot=casper integrity-check quiet splash ---
   initrd /casper/initrd
}

grub_platform
if [ "\$grub_platform" = "efi" ]; then
menuentry 'UEFI Firmware Settings' {
   fwsetup
}

menuentry "Test memory Memtest86+ (UEFI)" {
   linux /install/memtest86+.efi
}
else
menuentry "Test memory Memtest86+ (BIOS)" {
   linux16 /install/memtest86+.bin
}
fi
EOF
# generate manifest
dpkg-query -W --showformat='${Package} ${Version}\n' | sudo tee /image/casper/filesystem.manifest
cp -v /image/casper/filesystem.manifest image/casper/filesystem.manifest-desktop
sed -i '/ubiquity/d' /image/casper/filesystem.manifest-desktop
sed -i '/casper/d' /image/casper/filesystem.manifest-desktop
sed -i '/discover/d' /image/casper/filesystem.manifest-desktop
sed -i '/laptop-detect/d' /image/casper/filesystem.manifest-desktop
sed -i '/os-prober/d' /image/casper/filesystem.manifest-desktop
# create diskdefines
cat > /image/README.diskdefines << EOF
#define DISKNAME  Ubuntu from scratch
#define TYPE  binary
#define TYPEbinary  1
#define ARCH  amd64
#define ARCHamd64  1
#define DISKNUM  1
#define DISKNUM1  1
#define TOTALNUM  0
#define TOTALNUM0  1
EOF
cd /image/isolinux
# copy EFI loaders
cp /usr/lib/shim/shimx64.efi.signed.previous bootx64.efi
cp /usr/lib/shim/mmx64.efi mmx64.efi
cp /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed grubx64.efi
# create a FAT16 UEFI boot disk image containing the EFI bootloaders
dd if=/dev/zero of=efiboot.img bs=1M count=10
mkfs.vfat -F 16 efiboot.img
mmd -i efiboot.img efi efi/ubuntu efi/boot
mcopy -i efiboot.img ./bootx64.efi ::efi/boot/bootx64.efi
mcopy -i efiboot.img ./mmx64.efi ::efi/boot/mmx64.efi
mcopy -i efiboot.img ./grubx64.efi ::efi/boot/grubx64.efi
mcopy -i efiboot.img ./grub.cfg ::efi/ubuntu/grub.cfg
# grub BIOS image
grub-mkstandalone \
--format=i386-pc \
--output=core.img \
--install-modules="linux16 linux normal iso9660 biosdisk memdisk search tar ls" \
--modules="linux16 linux normal iso9660 biosdisk search" \
--locales="" \
--fonts="" \
"boot/grub/grub.cfg=isolinux/grub.cfg"
# bootable grub cdboot.img
cat /usr/lib/grub/i386-pc/cdboot.img core.img > bios.img
# generate md5sum.txt
/bin/bash -c "(find . -type f -print0 | xargs -0 md5sum | grep -v -e 'isolinux' > md5sum.txt)"
# clean up uuid and divert
truncate -s 0 /etc/machine-id
rm /sbin/initctl
dpkg-divert --rename --remove /sbin/initctl
# clean up chroot session
apt clean
rm -rf /tmp/* ~/.bash_history
umount /proc
umount /sys
umount /dev/pts
export HISTSIZE=0
exit
umount -R /mnt/*

Compress the chroot

After everything has been installed and preconfigured in the chrooted environment, we need to generate an image of everything that was done by following the next steps in the build environment.

mv /mnt/image .
# create squashfs
mksquashfs /mnt image/casper/filesystem.squashfs \
-noappend -no-duplicates -no-recovery \
-wildcards \
-comp xz -b 1M -Xdict-size 100% \
-e "var/cache/apt/archives/*" \
-e "root/*" \
-e "root/.*" \
-e "tmp/*" \
-e "tmp/.*" \
-e "swapfile"
# write the filesystem.size
printf $(sudo du -sx --block-size=1 /mnt | cut -f1) | sudo tee image/casper/filesystem.size
# create iso from the image directory using the command-line
cd image/
xorriso \
-as mkisofs \
-iso-level 3 \
-full-iso9660-filenames \
-J -J -joliet-long \
-volid "ubuntu-24.04.2-live-server-amd64" \
-output "../ubuntu-live-\$(date -u +%Y%m%d-%H%M%S).iso" \
-eltorito-boot isolinux/bios.img \
  -no-emul-boot \
  -boot-load-size 4 \
  -boot-info-table \
  --eltorito-catalog boot.catalog \
  --grub2-boot-info \
  --grub2-mbr /mnt/usr/lib/grub/i386-pc/boot_hybrid.img \
  -partition_offset 16 \
  --mbr-force-bootable \
-eltorito-alt-boot \
  -no-emul-boot \
  -e isolinux/efiboot.img \
  -append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b isolinux/efiboot.img \
  -appended_part_as_gpt \
  -iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 \
  -m "isolinux/efiboot.img" \
  -m "isolinux/bios.img" \
  -e '--interval:appended_partition_2:::' \
-exclude isolinux \
-graft-points \
   "/EFI/boot/bootx64.efi=isolinux/bootx64.efi" \
   "/EFI/boot/mmx64.efi=isolinux/mmx64.efi" \
   "/EFI/boot/grubx64.efi=isolinux/grubx64.efi" \
   "/EFI/ubuntu/grub.cfg=isolinux/grub.cfg" \
   "/isolinux/bios.img=isolinux/bios.img" \
   "/isolinux/efiboot.img=isolinux/efiboot.img" \
   "."
cd ..
rsync -a ubuntu-live.iso 192.168.16.1:/var/www/html/ubuntu-live/liveos/ubuntu-live3.iso

Ubuntu live

Procedure for creating Ubuntu live image. Currently the live image hangs after setvtrgb.service.

Environment setup

apt install -y debootstrap unzip
mkfs.ext4 /dev/vdb
mkdir /chroot
mount /dev/vdb /chroot
time debootstrap --arch=amd64 --variant=minbase plucky /chroot http://us.archive.ubuntu.com/ubuntu/
mount --make-rslave --rbind /proc /chroot/proc
mount --make-rslave --rbind /sys /chroot/sys
mount --make-rslave --rbind /dev /chroot/dev
mount --make-rslave --rbind /run /chroot/run
chroot /chroot /bin/bash

Chroot

  • configure apt sources
  • update the package indexes
  • null fstab
  • install systemd
  • setup uuid, divert for temporary chroot environment
cat > /etc/apt/sources.list << EOF
deb http://archive.ubuntu.com/ubuntu/ plucky main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ plucky main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ plucky-updates main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ plucky-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ plucky-security main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ plucky-security main restricted universe multiverse
EOF
apt update
cat /dev/null > /etc/fstab
apt install -y libterm-readline-gnu-perl systemd
# automate apt install locales
apt -y upgrade
apt install -y \
sudo \
ubuntu-standard \
dracut \
dracut-config-generic \
dracut-live \
discover \
laptop-detect \
os-prober \
network-manager \
net-tools \
wpagui \
grub-common \
grub-ipxe \
grub-gfxpayload-lists \
grub-pc \
grub-pc-bin \
grub2-common \
grub-efi-amd64-signed \
shim \
mtools \
binutils \
vim \
nano \
lsb-release \
build-essential \
less \
tree \
edac-utils \
moreutils \
util-linux-extra \
nvme-cli \
ipmitool \
ledmon \
inxi \
tmux \
grub2 \
unzip \
stressapptest \
openssh-server
apt install -y linux-firmware linux-image-`uname -r` linux-headers-`uname -r` linux-modules-extra-`uname -r`
apt autoremove -y
#apt install -y --no-install-recommends linux-generic

Configuration

timedatectl set-timezone America/Los_Angeles
sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' /etc/locale.gen
echo 'LANG=en_US.UTF-8' > /etc/default/locale
locale-gen
chmod -x /etc/update-motd.d/*
mv /etc/legal /etc/legal.disabled
touch /etc/NetworkManager/conf.d/10-globally-managed-devices.conf
resolvectl dns enp1s0 10.2.1.205
resolvectl dns enp1s0 10.2.1.225
sed -i 's/nm-online -s -q/nm-online -s -t 10/' /usr/lib/systemd/system/NetworkManager-wait-online.service
echo "ubuntu-fs-live" > /etc/hostname
cp --force /lib/systemd/system/[email protected] /etc/systemd/system/getty.target.wants/[email protected]
sed -i "s/ExecStart=.*/ExecStart=-\/sbin\/agetty --autologin root --noclear - \$TERM/g" /etc/systemd/system/getty.target.wants/[email protected]
for i in {2..6}; do cp --force /etc/systemd/system/getty.target.wants/[email protected] /etc/systemd/system/getty.target.wants/getty@tty$i.service; done
(echo 'supermicro'; echo 'supermicro') | passwd
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

https://manpages.ubuntu.com/manpages/plucky/man7/dracut.cmdline.7.html

Resize the virtual filesystem by remounting.

void-linux/void-mklive#202 (comment)

cat <<EOF >> /root/.bashrc
if [ $(($(df --output=source,size | grep LiveOS_rootfs | awk '{print $2}') / 1024 / 1024)) -lt 48 ]; then
    mount -o remount,size=48G /run
    echo "Remounting complete - /run 48G"
fi
if [ $(($(df --output=source,size,target | grep "/tmp" | awk '{print $2}') / 1024 / 1024)) -lt 24 ]; then
    mount -o remount,size=24G /tmp
    echo "Remounting complete - /tmp 24G"
fi
EOF

ISO files

mkdir -p /iso/{EFI,images,isolinux,LiveOS}
mkdir /iso/EFI/{boot,BOOT}
mkdir /iso/images/pxeboot
cp -p /boot/vmlinuz-**-**-generic /iso/images/pxeboot/vmlinuz
cp -p /boot/initrd.img-**-**-generic /iso/images/pxeboot/initrd.img
chmod 644 /iso/images/pxeboot/*
wget --progress=dot https://memtest.org/download/v7.00/mt86plus_7.00.binaries.zip -O /iso/images/memtest86.zip
unzip -p /iso/images/memtest86.zip memtest64.bin > /iso/images/memtest86+.bin
unzip -p /iso/images/memtest86.zip memtest64.efi > /iso/images/memtest86+.efi
rm -f /iso/images/memtest86.zip
# create grub configuration
cat > /iso/images/grub.cfg << EOF
set default="0"

function load_video {
  insmod efi_gop
  insmod efi_uga
  insmod video_bochs
  insmod video_cirrus
  insmod all_video
}

load_video
set gfxpayload=keep
insmod gzio
insmod part_gpt
insmod ext2

set timeout=60
### END /etc/grub.d/00_header ###

search --no-floppy --set=root -l 'Ubuntu-Server 25.04 amd64'

### BEGIN /etc/grub.d/10_linux ###
menuentry 'Start Ubuntu 25.04 Live' --class fedora --class gnu-linux --class gnu --class os {
    linuxefi /images/pxeboot/vmlinuz root=live:CDLABEL='Ubuntu-Server 25.04 amd64' rd.live.image quiet
    initrdefi /images/pxeboot/initrd.img
}
menuentry 'Test this media & start Ubuntu 25.04 Live' --class fedora --class gnu-linux --class gnu --class os {
    linuxefi /images/pxeboot/vmlinuz root=live:CDLABEL='Ubuntu-Server 25.04 amd64' rd.live.image rd.live.check quiet
    initrdefi /images/pxeboot/initrd.img
}

grub_platform
if [ "\$grub_platform" = "efi" ]; then
menuentry 'UEFI Firmware Settings' {
   fwsetup
}

menuentry "Test memory Memtest86+ (UEFI)" {
   linux /images/memtest86+.efi
}
else
menuentry "Test memory Memtest86+ (BIOS)" {
   linux16 /images/memtest86+.bin
}
fi
EOF
cp /usr/lib/shim/shimx64.efi.signed.previous /iso/images/bootx64.efi

apt clean
rm -rf /tmp/* ~/.bash_history
umount -l /proc
umount -l /sys
umount -l /dev/pts
export HISTSIZE=0
exit
umount -R /chroot/*

Compress the chroot

mksquashfs /chroot squashfs_$(date +%y%m%d%H%M).img -comp xz -Xbcj x86
mv squashfs_*.img ~/iso/LiveOS/squashfs.img
rm ubuntu-live*.iso
xorrisofs -o "./ubuntu-live-$(date -u +%Y%m%d-%H%M%S).iso" -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -b isolinux/isolinux.bin -c isolinux/boot.cat -boot-load-size 4 -boot-info-table -no-emul-boot -eltorito-alt-boot -e images/efiboot.img -no-emul-boot -isohybrid-gpt-basdat -R -J -V 'Ubuntu-Server 25.04 amd64' ~/iso/
rsync -a ubuntu-live*.iso [email protected]:/var/www/html/ubuntu-25.04-desktop-amd64/liveos/boot.iso
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment