UP | HOME

Minimal Ubuntu Install

Overview

This document discusses the how and why of creating a minimal Ubuntu Linux 24.04 (Noble Numbat) installation (referred to as MSR Ubuntu).

The goals of this lightly-customized Ubuntu are:

  1. Remove Ubuntu-specific technologies to make the user experience more broadly applicable to other Linux distributions.
  2. Increase the user's control over, knowledge of, and responsibility for the system and how it works.
  3. Maintain compatibility with standard Ubuntu, which is the best-supported Linux distribution for ROS 2.

Disclaimer

This document attempts to relay knowledge gleaned from experimenting and reverse-engineering parts of Ubuntu and its install process, as there is not (to my knowledge) and up-to-date tutorial explaining the process in detail. Therefore, it may contain factual errors.

This document also contains my opinions (particularly about packages that I would or would not include) in a minimal distribution. While I make a good attempt to explain the reasoning for and against each decision, the choices made are ultimately based on my experience and personal preferences. Reasonable people may come to different conclusions.

Setup

  1. These instructions are adapted from Ubuntu LiveCD Customization
  2. All commands are assumed to be run in order from a working directory. It is recommended that this directory starts out empty.

Working Copy of LiveCD

  1. Download the CD image.

    • Rather than the latest released image we use the pending daily image to ensure that files downloaded via debootstrap are from the same update as the files on the CD image.
    wget https://cdimage.ubuntu.com/noble/daily-live/pending/noble-desktop-amd64.iso
    wget https://cdimage.ubuntu.com/noble/daily-live/pending/SHA256SUMS
    wget https://cdimage.ubuntu.com/noble/daily-live/pending/SHA256SUMS.gpg
    
  2. Verify the image: How To Verify Ubuntu

    # Retrieve And Verify The Keys  (only do this once)
    gpg --keyid-format long --keyserver hkp://keyserver.ubuntu.com --recv-keys 0x46181433FBB75451 0xD94AA3F0EFE21092
    gpg --keyid-format long --list-keys --with-fingerprint 0x46181433FBB75451 0xD94AA3F0EFE21092
    # Ensure that the output shows the following:
    #pub   dsa1024/46181433FBB75451 2004-12-30 [SC]
    #    Key fingerprint = C598 6B4F 1257 FFA8 6632  CBA7 4618 1433 FBB7 5451
    #uid                  Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>
    #
    #pub   rsa4096/D94AA3F0EFE21092 2012-05-11 [SC]
    #    Key fingerprint = 8439 38DF 228D 22F7 B374  2BC0 D94A A3F0 EFE2 1092
    #uid                  Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>
    # Verify the iso
    gpg --keyid-format long --verify SHA256SUMS.gpg SHA256SUMS
    # You should see "gpg: Good Signature from "Ubuntu CD ..."
    
  3. Mount the iso image to a loopback device:

    # Setup loopback device, and store the device in loopdev variable
    export loopdev=$(sudo losetup --show -Pf noble-desktop-amd64.iso)
    # Mount the iso mount point and mount the iso
    mkdir isomnt
    sudo mount ${loopdev}p1 isomnt
    
  4. Copy the image, so that we can modify it (we can't change it in-place because .iso filesystems are read-only) and remove unneeded files

    cp -a isomnt extracted
    # Remove the files we don't need
    sudo rm extracted/casper/*.{de,en,es,fr,it,no-languages,pt,ru,zh}.*
    sudo rm extracted/casper/*secureboot*
    sudo rm extracted/casper/*filesystem*
    
  5. To reconstruct the filesystem we need the boot code from the Master Boot Record and EFI partition.

    • The MBR is the first sector (512 bytes) of the disk and the first 446 bytes of that constitute the boot code.
    • The EFI partition is the second partition on the ISO file
    # Copy the MBR
    dd bs=1 count=446 if=noble-desktop-amd64.iso of=mbr.img
    # Copy the EFI partition
    sudo cp ${loopdev}p2 EFI.img
    
  6. Setup the image to install a custom disk image, rather than one of the provided images:

    # Overrite the install-sources.yaml
    cat << EOF | sudo tee extracted/casper/install-sources.yaml
    - default: false
      description:
         en: Minimal Ubuntu for NUMSR.
      id: ubuntu-desktop-minimal
      locale_support: langpack
      name:
         en: Ubuntu (minimized)
      size: 0
      path: msr.squashfs
      type: fsimage-layered
      variant: minimal
    EOF
    

The System Image

These instructions discuss how to create the msr.squashfs filesystem that will be installed by subiquity (the Ubuntu Installer).

  • The msr.squashfs system is extracted to the harddrive and provides most of the starting packages for the new system
  • To create this partition, we download packages from the internet. If these packages end up not being from the same exact release/update as the packages on the CD image, problems can ensue when doing an offline installation.
  • Therefore, this step and the above step should be done at similar times.
  • Another potential workaround is to actually bootstrap packages from the CD rather than the internet, however not all bootstrap packages are actually archived on the CD. Theoretically, you could create your own Ubuntu mirror on the CD that has all packages needed for the image and installation.

First we create the base filesystem. make sure you have debootstrap installed=.

# Bootstrap the system
sudo debootstrap --arch=amd64 --variant=minbase \
--include=\
adduser,\
apt,\
apt-utils,\
bash-completion,\
console-setup,\
debconf,\
debconf-i18n,\
e2fsprogs,\
efibootmgr,\
grub-efi-amd64,\
grub-efi-amd64-signed,\
init,\
initramfs-tools,\
iproute2,\
iputils-ping,\
kbd,\
kmod,\
language-selector-common,\
less,\
linux-image-generic,\
locales,\
lsb-release,\
mawk,\
mount,\
netbase,\
passwd,\
procps,\
python3,\
sensible-utils,\
shim-signed,\
systemd-resolved,\
sudo,\
tzdata,\
ubuntu-keyring,\
udev,\
vim-tiny,\
whiptail,\
rsyslog,\
zstd \
noble msr_image

# Create a file that prioritizes the numsr-robotics repository
# This means that packages here will supersede other packages even if they
# are an older version of the package
# We also prevent the installation of netplan.io to prevent it's accidental installation.
# If our unpatched network-manager co-exists on a system with netplan.io, changes
# made in network-manager won't be synced with netplan
cat << EOF | sudo tee msr_image/etc/apt/preferences.d/numsr-apt-preferences
Package: *
Pin: release o=LP-PPA-numsr-robotics
Pin-Priority: 1020

Package: apport cloud-init netplan.io ubuntu-pro-client unattended-upgrades whoopsie
Pin: release *
Pin-Priority: -1
EOF

Next, we prepare to enter the filesystem as chroot, allowing us to start using the files here as if they were in our root directory.

sudo mount none -t proc msr_image/proc/
sudo mount none -t sysfs msr_image/sys/
sudo mount none -t devpts msr_image/dev/pts
sudo mount none -t tmpfs msr_image/tmp
sudo mount none -t tmpfs msr_image/run

# Make sure we can resolve hostnames in the chroot
sudo mkdir -p msr_image/run/systemd/resolve
sudo cp /etc/resolv.conf msr_image/run/systemd/resolve/stub-resolv.conf
sudo chroot msr_image

The following commands take place inside the chroot

# Install some improtant packages
apt update
apt install --no-install-recommends -y software-properties-common

# Add all ubuntu distribution components
add-apt-repository -y universe
add-apt-repository -y restricted
add-apt-repository -y multiverse

# Add the ppa.
# Many websites online say that add-apt-repository should not be used anymore, but we've come
# Full circle and now we should use add-apt-repository again!
add-apt-repository ppa:numsr/robotics -y

# Install the customized packages.
# Due to the pin we setup in the previous step, packages that are shipped with ubuntu
# will be overriden
apt install --no-install-recommends -y \
              network-manager \
              wpasupplicant \
              uncloud-init

# Make a network manager connection profile for eduroam
# The end user will have to modify this, but it will be
# Easier if most of the settings are already theire
nmcli --offline con add type wifi \
        con-name eduroam \
        autoconnect no \
        ssid eduroam \
        802-1x.eap peap \
        802-1x.identity NETID@northwestern.edu \
        802-1x.phase2-autheap mschapv2 \
        wifi-sec.auth-alg open \
        wifi-sec.key-mgmt wpa-eap \
        > /etc/NetworkManager/system-connections/eduroam.nmconnection
chmod 600 /etc/NetworkManager/system-connections/eduroam.nmconnection

# Disable phased updates
echo "APT::Get::Never-Include-Phased-Updates true;" > /etc/apt/apt.conf.d/99-Phased_Updates
# exit the chroot
exit

Finally we need to cleanup the chroot

sudo umount msr_image/proc
sudo umount msr_image/sys
sudo umount msr_image/dev/pts
sudo umount msr_image/tmp
sudo umount msr_image/run

Make the Minimal Squashfs

We now need to compress the filesystem image and add it to the install CD

  • Make sure you have squashfs-tools installed

Add the installable filesystem to the iso

# Make the squashfs filesystem from the chroot environment
# Warning, there should not be an existing msr.squashfs present: rm msr.squashfs
sudo mksquashfs msr_image msr.squashfs -comp xz

# Copy squashfs to casper
sudo cp msr.squashfs extracted/casper

Make the Desktop Squashfs

We can make an install ISO with msr.squashfs as the deployed filesystem. This will be a minimal Ubuntu system with network-manager and a user can therefore connect to the network (wirelessly if needed) and install other software. However, the full Ubuntu experience comes with an out-of-the-box Desktop environment, and in this section we will provide that to the install ISO user, as an option.

First, add an entry for a new squashfs:

# Append the install-sources.yaml
cat << EOF | sudo tee -a extracted/casper/install-sources.yaml
- default: true
  description:
    en: A minimal Ubuntu Desktop for NUMSR.
  id: ubuntu-desktop
  locale_support: langpack
  name:
    en: Ubuntu Desktop (NUMSR)
  size: 0
  path: msr.desktop.squashfs
  type: fsimage-layered
  variant: minimal
EOF

The goal is to make a new squashfs based off msr.squashfs without duplicating the files in msr.squashfs. For more details see Using Squashfs

  1. Make some new directories: mkdir msr_desktop msr_desktop.work msr_desktop.upper msr_image.mnt
    • msr_image.mnt is where msr.squashfs will be mounted read-only
    • msr_desktop is a directory where we will modify the combined filesystems
    • msr_desktop.upper contains the files that are in msr_desktop but not in msr_image.mnt
    • msr_desktop.work is the working directory for overlayfs
  2. Mount the overlay (assumes fuse-overlayfs is installed):

    sudo mount -t squashfs msr.squashfs msr_image.mnt -o loop
    sudo mount -t overlay \
     -o lowerdir=msr_image.mnt,upperdir=msr_desktop.upper,workdir=msr_desktop.work \
     overlay msr_desktop
    
  3. Mount all the filesystems needed for chroot and enter it

    sudo mount none -t proc msr_desktop/proc/
    sudo mount none -t sysfs msr_desktop/sys/
    sudo mount none -t devpts msr_desktop/dev/pts
    sudo mount none -t tmpfs msr_desktop/tmp
    sudo mount none -t tmpfs msr_desktop/run
    
    # Make sure we can resolve hostnames in the chroot
    sudo mkdir -p msr_desktop/run/systemd/resolve
    sudo cp /etc/resolv.conf msr_desktop/run/systemd/resolve/stub-resolv.conf
    sudo chroot msr_desktop
    
  4. Install all the desktop files: this is where you customize the installation, but my recommendations are in Desktop Setup.
  5. Exit the chroot
  6. Unmount the filesystems needed for the chroot:

    sudo umount msr_desktop/proc
    sudo umount msr_desktop/sys
    sudo umount msr_desktop/dev/pts
    sudo umount msr_desktop/tmp
    sudo umount msr_desktop/run
    
  7. Make the squashfs of the overlayed filesystem: sudo mksquashfs msr_desktop.upper msr.desktop.squashfs -comp xz
  8. Copy the squashfs to the iso: sudo cp msr.desktop.squashfs extracted/casper
  9. Unmount the overlay sudo umount msr_desktop
  10. Unmount the underlay sudo umount msr_image.mnt

Update the Live Squashfs

  1. The live squashfs (minimal.standard.live.squashfs) contains files that are available on the Live cd but not necessarily on the installed system
  2. We will add two utilities: clonezilla, which enables cloning harddrives and debootstrap which enables doing a bare-bones minimal installation.
  3. Extract the squashfs filesystems:

    sudo unsquashfs extracted/casper/minimal.squashfs && mv squashfs-root minimal.root
    sudo unsquashfs extracted/casper/minimal.standard.squashfs && mv squashfs-root minimal.standard.root
    sudo unsquashfs extracted/casper/minimal.standard.live.squashfs && mv squashfs-root minimal.standard.live.root
    
  4. Setup the overlays:

    mkdir minimal_standard.work minimal_standard.mnt
    # mount standard on top of live into minimal_standard.mnt
    sudo mount -t overlay \
     -o lowerdir=minimal.root,upperdir=minimal.standard.root,workdir=minimal_standard.work \
     overlay minimal_standard.mnt
    # minimal_standard.mnt now contains the merged contents of minimal.root and minimal.standard.root
    mkdir minimal_standard_live.work minimal_standard_live.mnt
    sudo mount -t overlay \
     -o lowerdir=minimal_standard.mnt,upperdir=minimal.standard.live.root,workdir=minimal_standard_live.work \
     overlay minimal_standard_live.mnt
    
    # minimal_standard_live.mnt is where we make modifications. The changes that only happened in the top layer will be in =minimal.standard.live.root=
    
  5. Mount all the filesystems needed for chroot and enter it

    sudo mount none -t proc minimal_standard_live.mnt/proc/
    sudo mount none -t sysfs minimal_standard_live.mnt/sys/
    sudo mount none -t devpts minimal_standard_live.mnt/dev/pts
    sudo mount none -t tmpfs minimal_standard_live.mnt/tmp
    sudo mount none -t tmpfs minimal_standard_live.mnt/run
    
    # Make sure we can resolve hostnames in the chroot
    sudo mkdir -p minimal_standard_live.mnt/run/systemd/resolve
    sudo cp /etc/resolv.conf minimal_standard_live.mnt/run/systemd/resolve/stub-resolv.conf
    sudo chroot minimal_standard_live.mnt
    
  6. apt install -y clonezilla debootstrap
  7. Exit the chroot with exit
  8. Unmount the filesystems needed for the chroot:

    sudo umount minimal_standard_live.mnt/proc
    sudo umount minimal_standard_live.mnt/sys
    sudo umount minimal_standard_live.mnt/dev/pts
    sudo umount minimal_standard_live.mnt/tmp
    sudo umount minimal_standard_live.mnt/run
    
  9. Make the squashfs of the overlayed filesystem: sudo mksquashfs minimal.standard.live.root minimal.standard.live.squashfs -comp xz
  10. Move the squashfs to the iso: mv minimal.standard.live.squashfs extracted/casper
  11. Unmount everything:

    sudo umount minimal_standard_live.mnt
    sudo umount minimal_standard.mnt
    

Recreate the ISO

# Re-create the iso.  This will create a .iso file called msr.iso
# WARNING! you must remove any existing msr.iso before running this command or it will fail
sudo xorriso -outdev msr.iso \
      -map extracted / -- \
      -volid "Ubuntu 24.04 MSR" \
      -boot_image grub grub2_mbr=mbr.img \
      -boot_image any partition_table=on \
      -boot_image any partition_cyl_align=off \
      -boot_image any partition_offset=16 \
      -boot_image any mbr_force_bootable=on \
      -append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b EFI.img \
      -boot_image any appended_part_as=gpt \
      -boot_image any iso_mbr_part_type=a2a0d0ebe5b9334487c068b6b72699c7 \
      -boot_image any cat_path='/boot.catalog' \
      -boot_image grub bin_path='/boot/grub/i386-pc/eltorito.img' \
      -boot_image any platform_id=0x00 \
      -boot_image any emul_type=no_emulation \
      -boot_image any load_size=2048 \
      -boot_image any boot_info_table=on \
      -boot_image grub grub2_boot_info=on \
      -boot_image any next \
      -boot_image any efi_path=--interval:appended_partition_2:all:: \
      -boot_image any platform_id=0xef \
      -boot_image any emul_type=no_emulation \
      -boot_image any load_size=full

# The above command came from reading the options to build the original .iso which can be done via
# sudo xoriso -indev ubuntu-desktop-24.04.iso -report_el_torrito cmd

Testing the Image in a Virtual Machine

  • During development, it is helpful to run the image in a virtual machine, in order to test it.
  • Install the open-source UEFI firmware for qemu: sudo apt install ovmf
  • Install the qemu emulator sudo apt install qemu-system-x86
  • Copy the UEFI firmware: cp /usr/share/OVMF/OVMF_CODE_4M.fd .
  • Create the hardisk: qemu-img create -f qcow2 test.img 20G
  • Run the emulator and boot the install cd (enable-kvm is optional but makes things faster)

qemu-system-x86_64 -drive if=pflash,format=raw,file=./OVMF_CODE_4M.fd -cdrom msr.iso -drive file=test.img -m 20G -enable-kvm -cpu host -smp 8

Structure of the Installation Image

The official Ubuntu Install Image is an iso0660 formatted disk image designed to be booted from either a CD or an external hard-drive. It enables users to test a Live Ubuntu 24.04 system and then do the installation.

The first sector of the iso image is the Master Boot Record (mbr). The first 446 bytes of the mbr is the boot code.

casper

The /casper directory contains various squashfs images that are either extracted as part of the LiveCD environment or the installation environment.

  • Essentially minimal.en.live.squashfs is the livecd
  • It seems like there is some hierarchy to the naming, whereas minimal.standard depends on minimal, for example.
    • I think these filesystems are extracted and overlayed somehow but I am not sure (if you know let me know!)
  • The kernel and initrd are also in /casper.
    • The /initrd can be extracted with unmkinitramfs. When its loaded the /init script is run.
    • Confusingly the initramfs is the replacement for the initrd, but it is still contained in a file called initrd!
    • The initrd and kernel are specified as part of the bootloader in /boot/grub/grub.cfg
  • The /casper/install-sources.yaml file tells the installation what variants of ubuntu to install.
    • For example, eliminating the second option removes the option for an extended ubuntu install when running the installer
    • Not quite sure how this maps however, have not fully tried.
    • Much of the content here is actually not needed when making a specific install
    • For example the langpacks. These are used to construct the name of the squashfs image, based off the base name, to install the desired squashfs.
    • For our purposes, we won't use language packs and just use a single squashfs that should be extracted.
  • The subiquity installer sets up configuration for curtin, the program that actually does the installation, based off of answers to questions.
  • curtin partitions the disks, extracts the squashfs to the disk (seeding the system), and then installs the kernel, the bootloader, and some other packages
    • It also generates locales, which is shwy some language packages are needed in the base image.
  • It does not setup a user account. Instead it sets up Ubuntu cloud-init to setup the user account on first boot
  • The curtin installer itself is inside a snap. To get to this snap
    1. unsquash the minimal.squashfs filesystem in casper
    2. Within that filesystem go to /var/lib/snapd/snaps/ubuntu-bootstrap.snap
      • A snap is just as squash-fs filesystem, so unsquash that.
      • You now have access to modifying/replacing curtin, which gives you ultimate flexibility

ISO disk file is a read-only disk format that contains a live Ubuntu 24.04 system and the subiquity install system. https://releases.ubuntu.com/noble/ubuntu-24.04-desktop-amd64.iso

Structure of the Live CD

Logs for the subiquity installer are stored in /var/log/installer. Looking at these logs is particularly helpful if the installer fails, to figure out why it failed.

Exploring Packages

  1. https://packages.ubuntu.com is a one-stop shop for exploring all the packages on any Ubuntu distribution.
  2. The apt-rdepends package is incredibly useful for recursively exploring package dependencies for a distribution:
    • apt-rdepends package shows all packages package depends on, recursively
    • apt-rdepends -r package shows all package that depend on a package. It is used to verify what packages may be uninstallable if a given package that is usually on an Ubuntu system is removed.
    • apt-file also provides a useful interface for getting information about packages
    • apt-policy can help you determine what version of a package is installed and what versions are available.

The Base System

The base system consists of

  1. The bootstrap: the bare minimal set of packages required to install other packages
  2. Minimal Userspace: utilities required to login and connect to the internet
  3. Desktop: the graphical user interface

Bootstrap

An Ubuntu system can be "bootstrapped" using the debootstrap tool.

To see the most basic packages for an Ubuntu userspace (mainly only useful for installing other packages) invoke: debootstrap --arch=amd64 --variant=minbase --print-debs noble <directory_name>.

MSR Ubuntu will contain all the minbase packages.

A system with just the bootstrap is essentially useless: all it has is the packages necessary for installing more packages.

Bootloader and Kernel

  • The Ubuntu install CD will install the bootloader files and kernel if they are not provided.
  • However, these packages are not included on the CD, so this will necessitate an internet install
  • To ensure a repeatable experience, it makes sense to install the following packages via debootstrap
    • Bootloader: efibootmgr, grub-efi-amd64, grub-efi-amd64-signed, shim-signed.
    • Kernel: linux-image-generic, initramfs-tools
    • We rely on dependency resolution to bring in the necessary dependencies of the above packages.
  • We also install zstd, a compression tool. This allows the setup to compress the initramfs with zstd instead of gzip, which is a better compression algorithm.

Minimal Ubuntu Userspace

Ubuntu defines a ubuntu-minimal package, which forms the basis of the minimal packages we will install.

However ubuntu-minimal depends on some packages that we do not want, and does not contain others that are essential for a minimal desktop.

With the tools of the minimal userspace installed, you can log in, connect to the internet, and install more packages. Of course, you will need to do this all at a console, as there is no graphical user interface (GUI) or desktop.

Ubuntu Minimal

The ubuntu-minimal package is a meta-package that no other packages depend on. Since we won't have all the packages in ubuntu-minimal we will not install it. Instead we will manually pick and install the dependencies of ubuntu-minimal that we want.

Netplan

Netplan (package: netplan.io) is method for configuring Linux networks using YAML files and is part of ubuntu-minimal. Overall, this package is being removed because it is mainly only used on Ubuntu and makes debugging networking more difficult.

  • Reasons for Keeping Netplan
    1. Ubuntu created Netplan to unify network configuration between NetworkManager and systemd-networkd (the only two supported backends as of 2024).
    2. Netplan reads a YAML configuration file and translates that into a configuration used by whichever backend is chosen.
    3. Netplan does not yet have wide adoption outside Ubuntu (although it can be used in Debian and the default for Debian Cloud.
    4. On desktop Ubuntu, the use of netplan is transparent because configurations of netplan and NetworkManager are bi-directionally synchronized.
      • This means, in theory, you can use NetworkManager commands and not worry about netplan or netplan configuration and not worry about NetworkManager
    5. It is the default on all versions of Ubuntu and is difficult to remove due to packages that depend on it.
  • Reasons for Removing Netplan
    1. I have not encountered a use-case where I need a unified configuration between NetworkManager and systemd-networkd
      • If the network setup is complicated or the computer is used in different networking environments (e.g., it is a laptop) I use NetworkManager
      • If the network setup is simple I use NetworkManager too.
      • If I ever couldn't use NetworkManager (maybe it's too big and complicated), I'd use systemd-networkd.
      • It is hard to imagine a situation where I would need to setup a network on multiple machines where one machine used NetworkManager, the other used systemd-networkd, and the setup was so complicated that having a single configuration file would be beneficial.
    2. Everything that netplan can do, NetworkManager can do (except read a YAML configuration file).
    3. There are network setups that netplan can not accomplish or that it can only accomplish when using the NetworkManager backend.
    4. With netplan there are now two systems to worry about (when there used to be only one):
      • When debugging problems, you often need to understand how netplan configuration maps to NetworkManager or systemd-networkd configuration.
      • You need to worry about the two systems being out of sync (either through bugs or user error).
    5. Skills learned with NetworkManager transfer to other Linux distributions much more readily than learning about netplan
      • As a result, there are far more resources online with guidance on how to use NetworkManager. No need to stick with only Ubuntu-based help.
    6. The numsr ppa contains a replacement network-manager package that reverses the Ubuntu-specific patches that synchronize it with netplan
      • I also removed Debian/Ubuntu specific configuration of network-manager that allowed systems to mix its use with the old network management system ifupdown. This change is consistent with the goal of having only one network configuration to use, learn, and debug when something goes wrong.

Ubuntu Pro Client

Ubuntu Pro is a paid version of Ubuntu (although it is free in certain cases) with additional updates and support. We will not use Ubuntu Pro and therefore do not need this package.

  • Reasons for Keeping
    1. There are some packages (particularly meta packages) that depend on on ubuntu-pro-client.
    2. Canonical (the company behind Ubuntu) needs to make money somehow.
    3. Ubuntu Pro does appear to have some useful features (for example they provide a pre-compiled realtime Linux kernel).
  • Reasons for Removing
    1. We do not use its features.
    2. It is related to things that will (lightly and respectfully) nag you about getting Ubuntu Pro
    3. There are a few solutions for the packages that depend on ubuntu-pro-client
      1. Don't install those packages (none of them are necessary)
      2. Install a dummy package that does nothing but satisfies the dependency. Assume that most (if not all) programs that depend on ubuntu-pro-client mostly function even without it.
      3. Block installation of ubuntu-pro-client in /etc/apt/preferences.d. This is the option this installation uses, but if you ever need to undo it, just edit the preference.

Cloud-init

Cloud-init is Ubuntu's method of provisioning containers (and other cloud computers) using YAML configuration files. In Ubuntu 24.04, they also use cloud-init as part of the desktop.

  • Reasons for Using Cloud Init
    1. It is the default for Ubuntu 24.04
    2. It is hard to replace because it does essential tasks (such as creating the user account) on the first boot
  • Reasons for Removing Cloud Init
    1. We are not provisioning cloud-servers, we are making a single laptop computer
    2. It runs on every boot, even though in our case it only does work on the first boot
    3. The tasks it appears to be doing, in the role it plays in the Desktop, can be easily accomplished in a simpler way
    4. It brings in a lot of dependencies that are not necessary.
    5. It is an Ubuntu-specific technology.

Additional Packages

  1. language-selector-common is used by curtin (I don't know exactly what it does but it seems to setup languages
  2. network-manager (from the nu-msr ppa) is used to configure networking
  3. software-properties-common tools for managing apt repositories
  4. systemd-resolved is used by NetworkManager for resolving DNS queries
  5. I also add a configuration file /etc/NetworkManager/system-connections that makes it easier to connect to eduroam WiFi networks

Blocked packages

To avoid accidentally installing certain packages that are unwanted, the installation blocks them using /etc/apt/preferences.d/numsr-apt-preferences.

The packages that are blocked are:

  1. apport Ubuntu-specific core dump utility (other Linux uses systemd-coredump)
  2. cloud-init
  3. netplan.io
  4. ubuntu-pro-client
  5. unattended-upgrades
  6. whoopsie

Additional Package Options

Phased Updates are disabled, ensuring that we are all on more-similar Ubuntu versions.

  • From man 5 apt_preferences setting Never-Include_Phased-Updates prevents our machines from getting them.

Desktop

Technically from the minimal system you can install whatever desktop environment that you would like. However, in the spirit of making the install experience as close to stock-ubuntu as possible, I install the same desktop system (minus a few things) that you would expect.

The goal here is not a minimal desktop,rather it is a desktop that closely matches what is standard in Ubuntu, just without parts that conflict with the rest of the mission of this installation (e.g., removing auto update, not using snaps, etc).

It is possible, after installation, to install your own desktop environment or build from scratch upon the minimal base.

The apt install command below installs most of the packages from ubuntu-desktop-minimal except for:

Required

Packages that are NOT included

  1. ca-certificates (already installed)
  2. software-properties-gtk (we manage software via the commandline and it has undesirable dependencies)
  3. ubuntu-release-upgrader-gtk (we do not want to ever upgrade the release).
  4. update-manager (we manage updates via the command-line)
  5. update-notifier (do not want to be nagged about updating)
  6. apport-gtk (we don't use apport. It can be useful for crash dumps but it is Ubuntu only and the systemd-coredump is more widely supported on Linux)
  7. avahi-daemon (mDNS is handled by systemd-resolvd via NetworkManager, so this is redundant and causes confusion)
  8. firefox (we want a non-snap version provided by Mozilla directly)
  9. language-selector-common (already installed) \
  10. libglib2.0-bin (already installed) \
  11. network-manager-* (network-manager is part of the minimal system).
  12. xkb-data (already installed)

Recommended

  • Lots of the suggested packages are there as "just in case your computer has X hardware".
  • I have culled these partially based on what hardware is available on the Sys76 Adder4 laptop

Packages that are NOT included

  1. gamemode (this is a daemon that lets applications use it, but does not seem to be running. if anything needs it they will bring it in as a dependency)
  2. gnome-initial-setup (the installer already has setup gnome)
  3. libpam-fprintd (laptop does not have a finger print reader)
  4. package-kit (we manage packages from the commandline)
  5. printer-driver-* (if needed install the required package)
  6. pcmciautils (our laptops don't have pcmcia
  7. libproxy1-plugin-gsettings (we do not use libproxy)
  8. libproxy1-plugin-networkmanager (we do not use libproxy)
  9. snapd (we do not use snaps)
  10. ubuntu-report (do not want to send telemetry to Canonical, Linux should be telemetry free!)
  11. whoopsie (do not want pop-ups asking us to report errors to Ubuntu when developing our own software that may crash frequently!)

Additional Packages

Since the move to snaps, there are no mainstream web-browsers that are regular apt packages. We will install firefox from the Official Mozila APT Repository

  • dbus-x11: when installing the system76-driver part of the mkinitcpio process seems to want it.

The Commands in the Chroot

The command should be run in the chroot when creating the squashfs.

  • To determine what was already installed, I ran this command without -y and looked at the apt output
  • Packages like whoopsie and apport are in the recommended list but will not be installed due to the pin we set up earlier.
apt update
apt install -y \
  alsa-base \
  alsa-utils \
  anacron \
  at-spi2-core \
  bc \
  dbus-x11 \
  dmz-cursor-theme \
  fontconfig \
  fonts-dejavu-core \
  foomatic-db-compressed-ppds \
  gdm3 \
  ghostscript \
  gnome-control-center \
  gnome-menus \
  gnome-session-canberra \
  gnome-settings-daemon \
  gnome-shell \
  gnome-shell-extension-appindicator \
  gnome-shell-extension-desktop-icons-ng \
  gnome-shell-extension-ubuntu-dock \
  gnome-shell-extension-ubuntu-tiling-assistant \
  gstreamer1.0-alsa \
  gstreamer1.0-packagekit \
  gstreamer1.0-plugins-base-apps \
  inputattach \
  language-selector-gnome \
  libatk-adaptor \
  libnotify-bin \
  libsasl2-modules \
  libu2f-udev \
  nautilus \
  openprinting-ppds \
  pipewire-pulse \
  printer-driver-pnm2ppa \
  rfkill \
  spice-vdagent \
  ubuntu-drivers-common \
  ubuntu-session \
  ubuntu-settings \
  unzip \
  wireless-tools \
  wireplumber \
  xdg-user-dirs \
  xdg-user-dirs-gtk \
  xorg \
  yelp \
  zenity \
  zip \
  appstream \
  apt-config-icons-hidpi \
  baobab \
  bluez \
  bluez-cups \
  cups \
  cups-bsd \
  cups-client \
  cups-filters \
  dirmngr \
  eog \
  evince \
  fonts-liberation \
  fonts-noto-cjk \
  fonts-noto-color-emoji \
  fonts-noto-core \
  fonts-ubuntu \
  fwupd \
  fwupd-signed \
  gir1.2-gmenu-3.0 \
  gnome-accessibility-themes \
  gnome-bluetooth-sendto \
  gnome-calculator \
  gnome-characters \
  gnome-clocks \
  gnome-disk-utility \
  gnome-font-viewer \
  gnome-keyring \
  gnome-logs \
  gnome-power-manager \
  gnome-remote-desktop \
  gnome-system-monitor \
  gnome-terminal \
  gnome-text-editor \
  gpg-agent \
  gsettings-ubuntu-schemas \
  gvfs-fuse \
  hplip \
  ibus \
  ibus-gtk \
  ibus-gtk3 \
  ibus-table \
  im-config \
  kerneloops \
  laptop-detect \
  libnss-mdns \
  libpam-gnome-keyring \
  libpam-sss \
  libspa-0.2-bluetooth \
  libwmf0.2-7-gtk \
  memtest86+ \
  mousetweaks \
  nautilus-sendto \
  orca \
  plymouth-theme-spinner \
  policykit-desktop-privileges \
  seahorse \
  speech-dispatcher \
  systemd-oomd \
  ubuntu-docs \
  ubuntu-wallpapers \
  xcursor-themes \
  xdg-desktop-portal-gnome \
  xdg-utils \
  yaru-theme-gnome-shell \
  yaru-theme-gtk \
  yaru-theme-icon \
  yaru-theme-sound

We will install Firefox from the Mozilla apt repository (adapted from Mozilla Documentation:

# Get mozilla's signing key
wget -q https://packages.mozilla.org/apt/repo-signing-key.gpg -O /etc/apt/keyrings/packages.mozilla.org.asc

# Verify that it's fingerprint is 35BAA0B33E9EB396F59CA838C0BA5CE6DC6315A3
gpg -n -q --import --import-options import-show /etc/apt/keyrings/packages.mozilla.org.asc

echo "deb [signed-by=/etc/apt/keyrings/packages.mozilla.org.asc] https://packages.mozilla.org/apt mozilla main" > /etc/apt/sources.list.d/mozilla.list

# Ensure this firefox takes precedence over the fake apt package
cat << EOF > /etc/apt/preferences.d/mozilla
Package: *
Pin: origin packages.mozilla.org
Pin-Priority: 1000
EOF

apt update
apt install firefox

Updating the Install ISO

  • The instructions for updating the ISO are below
  • An explanation of the install iso is after the instructions
  • Before you begin: the version of packages from debootstrap must match that version of packages on the installation media or the install will fail. This means if you downloaded
  • These instructions are a beta version.

Writing The ISO To a USB Drive:

  • See Archlinux USB Flash
  • 1. Use lsblk to find the name of the flash drive and ensure that it is not mounted
    1. I like to sudo chown <user>:<user> name/of/flashdrive so I can't accidentally overwrite the wrong drive
    2. dd bs=4M if=msr.iso of=/dev/<path_to_device> conv=fsync oflag=direct status=progress

MSR PPA

The following steps should be done from Ubuntu LTS

How To Make A PPA

  1. Sign up for a launchpad.net account
  2. Create an OpenPGP key: gpg --full-gen-key
  3. See the keys with gpg --list-keys
  4. Send your key to the Ubuntu keyserver: gpg --send-keys --keyserver keyserver.ubuntu.com <keyid>
  5. Wait 10 minutes for the server to update
  6. In your launchpad profile, add the pgp key (use gpg --fingerprint to get the fingerprint you needed)
  7. Assuming your email is not setup with gpg, copy the encrypted message to a file and decrypt with gpg -d, then follow the instructions.
  8. While logged into launchpad, assign (or affirm) the Code of Conduct

How To Rebuild a Debian Package

  1. Make sure the pgp keys are setup on the system you are building the package on.
  2. Install the devscripts package
  3. Create /etc/apt/sources.list.d/ubuntu-src.sources to let =apt know where to get source packages from.

    Types: deb-src
    URIs: http://archive.ubuntu.com/ubuntu/
    Suites: noble noble-updates noble-backports
    Components: main universe restricted multiverse
    Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
    
  4. sudo apt update
  5. apt build-dep <packagename> installs the build dependencies
  6. Create a working directory <workdir> and cd <workdir>.
  7. apt source <packagename> downloads the source package to <workdir>.
  8. The debian build process likes to spew and refer to files in the parent directory.
    • To help with this it makes sense to copy the workdir when issuing a new debuild command.
  9. The package download should have created a directory with the upstream package name and version <package>. Enter this directory
  10. Most debian packages use the Quilt patch management system to manage patches
    • Use quilt push -a to make sure the package is up-to-date
    • You can also track things as patches using quilt if you would like:
      • quilt new patchname.patch creates a new patch
      • quilt add filename adds a file to the patchset. This must be done prior to editing the file
      • When done editing use quilt refresh to apply the patch.
  11. Edit debian/changelog, adding a new entry for yourself outlining the changes you made
    • Be sure to boost the version number in the changelog.
    • If you have just made changing to the packaging, change the debian version (the part after the -)
    • Appending a ~<N> makes the package be considered less new than the version without the ~<N> appended.
    • Name and email address should likely be consistent with the pgp key you use to sign the ppa.
    • These packages are very finicky about the version naming scheme, and various items have different semantic meanings. For my purpose, incrementing the revision number seems to work best.
  12. The debian/control has information about package dependencies
  13. When ready to build run debuild -k<your-pgp-key-id> -b (from a new <workspace> directory)
    • You may wish to install and test the package.
  14. When ready to upload to the ppa run: debuild -k<your-pgp-key-id> -S -sa
  15. If the source built successfully upload the packages using dput ppa: <package_name>.changes (according to the instructions on the ppa page).
  16. Refresh your ppa homepage. It may take a little while. If the build failed, you will get an email with some diagnostic information.
  17. There is no need to keep track of your changes in git: once uploaded to launchpad you can see the source and your changes to it by downloading the source package.
    • Essentially, your changes are being tracked in launchpad. This is fine for changes to the packaging itself, but if you are changing source code you likely want to make the changes in a fork or even submit them to the upstream project.

Git and quilt

Quilt is used in debian packages to keep track of "upstream debian changes" and the source code version. I like to fork a package and make changes in the fork. I then use "git format-patch <commit>" to turn the commits to HEAD from <commit> into a patchset In the original debain package (say it's version 2.44), I then use "quilt import" on each patch created by git format-patch. Now the debian source package contains the patches made against the upstream version to debianize it, but meanwhile all my code is in git Quilt environment variable QUILT_PATCHES=debian/patches should be set

RT Kernel

  1. Get RT Kernel sources (must get via git to make the debian source package) git clone https://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-stable-rt.git/ CHeckout the latest rt branch
  2. Make menu config and setup the kernel.
    • I made a custom version based on the config available in /boot/config
    • The kernel is designed for x86 systems and removes support for a lot of legacy hardwaree tc
    • It's nto fully minimal as it tries to stay close to ubuntu defaults
  3. In kernel source tree: make menuconfig
  1. To build a debian package: make deb-pkg
    • Test this by installing it
  2. To prepare for the ppa
  3. Use make srcdeb-pkg to make the source package
    • This creates a gzip of the package directory.
    • The original source is the orig.tar.gz file
  4. What we need to do is
    1. Extract the orig.tar.gz file to a file that matches the name of what you actually want to call the package
  5. Unpack the source package to a new directory
  6. tar czvf name_of_package.orig.tar.gz source_dir
    • This creates the original tarball
  7. Extract the source tarball into the extracted source directory
  8. Edit control and changelog to set the version and the package name that you want
  9. debuild -k<KEYID> -S -sa to build the source package that can be uploaded to the ppa

Author: Matthew Elwin.