Instructions how to install Ubuntu using debootstrap. Below instructions were verified to work with debootstrapping Ubuntu 24.04.2.
- Table of contents
- Conventions
- Essential steps
- Install debootstrap
- Prepare disk for installing boot loader
- Set up filesystem for Ubuntu
- Mount filesystem
- Install base system
- Chroot into installed base system
- Set up editor
- Configure apt sources
- Install systemd
- Null the fstab file
- Machine-id and divert
- Upgrade packages
- Install Live System packages
- Choose timezone
- Configure locales
- Install new kernel
- Remove old kernel
- Remove unused packages
- Set a custom hostname
- Set root's password
- Install ssh-server and enable root login
- Install additional software
- Enable
os_prober
ingrub
- Finish installation
/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
)
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
BIOS systems require different steps than UEFI 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 forDisklabel 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 forPartition Table:
Partition Table: gpt
means GPT.Partition Table: msdos
means MBR.Partition Table: unknown
probably means partition table is missing.
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:~#
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.
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
E.g.
mkfs.ext4 /dev/vdb
mount /dev/vdb /mnt
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/
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
nano
: should already be there as defaultvim
:
apt install -y vim nano
# update-alternatives --config editor
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
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.
cat /dev/null > /etc/fstab
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.
apt -y upgrade
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
# dpkg-reconfigure tzdata
timedatectl set-timezone America/Los_Angeles
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 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`
# 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*
apt autoremove -y
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
(echo 'supermicro'; echo 'supermicro') | passwd
# enable root login over ssh
apt install -y openssh-server
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
Install the equivalent of "Development Tools"
apt install -y build-essential less tmux
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
We are now back in our build environment
after setting up our live system
and will continue creating files necessary to generate the ISO.
- Create directories
mkdir -p /image/{casper,isolinux,install}
- Copy kernel images
cp /boot/vmlinuz-**-**-generic /image/casper/vmlinuz
cp /boot/initrd.img-**-**-generic /image/casper/initrd
- 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
- Create base point access file for grub
touch /image/ubuntu
- 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
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
.
- 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
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.
- 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
# 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
- 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
- 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"
- Combine a 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)"
- Each machine should have a unique identifier so be sure to run
truncate -s 0 /etc/machine-id
- Remove the diversion
rm /sbin/initctl
dpkg-divert --rename --remove /sbin/initctl
- Clean up
apt clean
rm -rf /tmp/* ~/.bash_history
umount /proc
umount /sys
umount /dev/pts
export HISTSIZE=0
exit
umount -R /mnt/*
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 ..