2

I followed this link to create an offline autoinstall for Ubuntu 22.04.

I have successfully generated the ISO using xorriso and installed it in a virtual machine on VirtualBox, however after rebooting the machine fails to boot into normal mode. Instead I am presented with a minimal BASH-like line editing shell, as shown below.

grub> ls
(proc) (hde) (hd0,gpt3) (hd0,gpt2) (hd0,gpt1)
grub> ls (hd0
Possible partitions are:

Device hd0: No known filesystem detected - Sector size 512B - Total size 41943040KiB Partition hd0,gpt1: No known filesystem detected - Partition start at 1024KiB - Total size 1024KiB Partition hd0, gpt2: Filesystem type ext Last modification time 2024-11-12 06:37:13 Tuesday, UUID f0022119-d86e-4f00-b6ef-3d5a3f604cd7 Partition start at 2048KiB - Total size 2097152KiB Partition hd0, gpt3: No known filesystem detected - Partition start
at 2099200KiB - Total size 39842816KiB

grub> ls (hd0,gpt2)/ Possible files are:

lost+found/ grub/ grub> ls (hd0,gpt2)/_

The ISO was generated using the following command:

xorriso -as mkisofs -r -V 'Ubuntu-22.04-LTS-AUTO' -o ../ubuntu-nov12-test1-autoinstall.iso --grub2-mbr ../BOOT/1-Boot-NoEmul.img -partition_offset 16 --mbr-force-bootable -append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b ../BOOT/2-Boot-NoEmul.img -appended_part_as_gpt -iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 -c 'boot.catalog' -b 'boot/grub/i386-pc/eltorito.img' -no-emul-boot -boot-load-size 4 -boot-info-table -eltorito-alt-boot -e '--interval:appended_partition_2:::' -no-emul-boot .

grub.cfg file is as follows:

set timeout=30
loadfont unicode

set menu_color_normal=white/black set menu_color_highlight=black/light-gray

menuentry "Try or Install Ubuntu Server" { set gfxpayload=keep linux /casper/vmlinuz autoinstall ds=nocloud;s=/cdrom/nocloud/ --- initrd /casper/initrd }

if [ "$grub_platform" = "efi" ]; then menuentry 'Boot from next volume' { exit 1 } menuentry 'UEFI Firmware Settings' { fwsetup } else menuentry 'Test memory' { linux16 /boot/memtest86+.bin } fi

install-sources.yaml file is as follows:

$ cat install-sources.yaml 
- description:
    en: This version installs a Desktop environment where humans are not expected to log in.
  id: desktop
  locale_support: none
  name:
    en: Ubuntu Desktop
  path: filesystem.squashfs
  size: 2712014848
  type: fsimage
  variant: server
  default: true
  • description: en: This version has been customized to have a small runtime footprint in environments where humans are not expected to log in. id: ubuntu-server-minimal locale_support: none name: en: Ubuntu Server (minimized) path: ubuntu-server-minimal.squashfs size: 611012608 type: fsimage variant: server

  • description: en: The default install contains a curated set of packages that provide a comfortable experience for operating your server. id: ubuntu-server locale_support: locale-only name: en: Ubuntu Server path: ubuntu-server-minimal.ubuntu-server.squashfs size: 1259954176 type: fsimage-layered variant: server

@mpboden I followed your instructions and took the steps below

apt update
sudo apt install xorriso squashfs-tools gzip wget p7zip-full -y

Unpacked the Iso`s

mkdir server-iso-extracted desktop-iso-extracted

7z -y x ~/server-iso-extracted/ubuntu-22.04.5-live-server-amd64.iso -oserver-iso/ cd server-iso/ mv '[BOOT]' ../BOOT

7z -y x ~/desktop-iso-extracted/ubuntu-22.04.5-desktop-amd64.iso -odesktop-iso/

Unsquash the Desktop Filesystem

sudo unsquashfs -d desktop-squashfs desktop-iso-extracted/desktop-iso/casper/filesystem.squashfs

Chroot into the unsquashed filesystem

sudo mount --bind /dev desktop-squashfs/dev sudo mount --bind /proc desktop-squashfs/proc sudo mount --bind /run desktop-squashfs/run sudo mount --bind /sys desktop-squashfs/sys sudo chroot desktop-squashfs

Inside the chroot, run custom scripts to install additional packages (Apache, PHP, PostgreSQL):

Example commands for package installation

apt update apt install -y apache2 php postgresql

Install cloud-init for user configuration during autoinstall

apt install -y cloud-init

#Exit chroot and clean up

exit sudo umount desktop-squashfs/dev sudo umount desktop-squashfs/proc sudo umount desktop-squashfs/run sudo umount desktop-squashfs/sys

Resquash the Modified Filesystem

sudo mksquashfs desktop-squashfs server-iso-extracted/server-iso/casper/filesystem.squashfs -comp xz -e boot

Add Autoinstall Files to Server ISO Directory

mkdir -p server-iso-extracted/server-iso/nocloud touch user-data meta-data

vi user-data #cloud-config autoinstall: version: 1 identity: hostname: ubuntu-desktop password: $6$5lpwCLsKLEzMkSJc$keOAhA6aO/5RocGThmhVA7LSNuW911Rx5HHXFEa75oGK20cEdAAgn14H5f5nGeq6QgcSyLPrWcg1.JvjXbhrN/ realname: Ubuntu user username: ubuntu timezone: Asia/Kolkata network: version: 2 renderer: NetworkManager refresh-installer: update: no keyboard: layout: us locale: en_US.UTF-8 late-commands: - curtin in-target --target=/target -- apt-get install -y cloud-init

grub.cfg file is given above install-sources.yaml file is given above

#Update checksums:

cd ~/server-iso-extracted/server-iso sudo chmod +w md5sum.txt sudo find . -type f -print0 | sudo xargs -0 md5sum | grep -v "./md5sum.txt" | sudo tee md5sum.txt md5sum -c md5sum.txt

The iso was generated using the xorriso command given above.

karel
  • 122,292
  • 133
  • 301
  • 332
Rajaram
  • 21

1 Answers1

1

The exact steps you took are a bit hard to follow. Therefore, I've outlined the steps below that I use to create a new ISO configured with autoinstall.

Some observations first:

  • Your grub.cfg file does not match mine exactly, but I was successful with yours
  • Your install-sources.yaml file does not match. But I don't see a problem here. I just kept mine simple.
  • Your user-data file is slightly different than mine. I have timezone: Asia/Kolkata indented within another user-data directive. But testing shows it works both ways. Also, you're running a late-command to install cloud-init, but if you're un-squashing the Desktop squashfs and installing cloud-init within the chroot, then you don't need to run this late command during autoinstall; it's redundant.

Some notes:

  • We're downloading both the Server and Desktop ISO's for Ubuntu 22.04.5
  • The full Server ISO is extracted
  • Only the /casper/ directory is extracted from the Desktop ISO
  • The Desktop filesystem.squashfs is un-squashed, chrooted, modified, re-squashed, and copied to the extracted server ISO
  • Necessary files are created for autoinstall in the extracted server directory
  • grub.cfg and install-sources.yaml are modified within the extracted server directory
  • The extracted server directory is re-packed into a new ISO
  • This process adds the squashfs filesystem from the Ubuntu Desktop ISO to the Server ISO instead of installing the ubuntu-desktop package into the Server installation. With this technique, Subiquity from the Server ISO is used for installation.

Within a working directory, make the following directories:

$ mkdir server-iso-extracted desktop-iso-extracted tmp

Download ISO files:

wget https://nl3.releases.ubuntu.com/releases/releases/releases/releases/22.04/ubuntu-22.04.5-live-server-amd64.iso
wget https://nl3.releases.ubuntu.com/releases/releases/releases/releases/22.04/ubuntu-22.04.5-desktop-amd64.iso

Current working directory structure:

$ tree -L 1
.
├── desktop-iso-extracted
├── server-iso-extracted
├── tmp
├── ubuntu-22.04.5-desktop-amd64.iso
└── ubuntu-22.04.5-live-server-amd64.iso

Unpack the ISO's. For the Desktop ISO, you only need the /casper/ directory.

xorriso -osirrox on -indev ubuntu-22.04.5-live-server-amd64.iso -extract / ./server-iso-extracted/
xorriso -osirrox on -indev ubuntu-22.04.5-desktop-amd64.iso -extract /casper/ ./desktop-iso-extracted/

Copy Desktop filesystem.squashfs to tmp directory:

cp desktop-iso-extracted/filesystem.squashfs ./tmp/

Unsquash desktop-iso-extracted/casper/filesystem.squashfs from Desktop ISO:

sudo unsquashfs -n -d tmp/squashfs-root tmp/filesystem.squashfs

Create chroot:

sudo mount --bind /etc/resolv.conf tmp/squashfs-root/etc/resolv.conf
sudo mount -t proc none tmp/squashfs-root/proc
sudo mount -t sysfs none tmp/squashfs-root/sys
sudo mount -t devpts none tmp/squashfs-root/dev/pts
sudo chroot tmp/squashfs-root/

Modify Desktop filesystem. Upgrade and install cloud-init:

apt update
apt install cloud-init
# install any other applications as well

Exit chroot:

exit
sudo umount tmp/squashfs-root/proc
sudo umount tmp/squashfs-root/sys
sudo umount tmp/squashfs-root/dev/pts
sudo umount tmp/squashfs-root/etc/resolv.conf

Create a new modified squashfs:

sudo mksquashfs tmp/squashfs-root/ tmp/modified_filesystem.squashfs -comp xz

Copy modified squashfs to server-iso-extracted/casper/filesystem.squashfs:

sudo cp tmp/modified_filesystem.squashfs server-iso-extracted/casper/filesystem.squashfs

Create a server-iso-extracted/nocloud directory:

mkdir server-iso-extracted/nocloud

Create a meta-data file:

touch server-iso-extracted/nocloud/meta-data

Create a server-iso-extracted/nocloud/user-data file with the following contents:

#cloud-config
autoinstall:
  version: 1
  # The following credentials will create a default user. username: ubuntu - password: ubuntu 
  identity:
    hostname: ubuntu-desktop
    password: $6$5lpwCLsKLEzMkSJc$keOAhA6aO/5RocGThmhVA7LSNuW911Rx5HHXFEa75oGK20cEdAAgn14H5f5nGeq6QgcSyLPrWcg1.JvjXbhrN/
    realname: Ubuntu user
    username: ubuntu
  user-data:
    timezone: America/Los_Angeles
  # Let NetworkManager manage all devices on this system
  network:
    version: 2
    renderer: NetworkManager
  refresh-installer:
    update: no
  keyboard:
    layout: us
    toggle: null
    variant: ''
  locale: en_US.UTF-8
# cloud-init is not installed by default in the Desktop ISO. Therefore, it either needs to be installed within the ISO or installed with the following lines:
#  late-commands:
#  - curtin in-target --target=/target -- apt install -y cloud-init

Give write permissions to server-iso-extracted/boot/grub/grub.cfg:

chmod +w server-iso-extracted/boot/grub/grub.cfg

Modify server-iso-extracted/boot/grub/grub.cfg as follows:

set timeout=30

loadfont unicode

set menu_color_normal=white/black set menu_color_highlight=black/light-gray

menuentry "Try or Install Ubuntu Server" { set gfxpayload=keep linux /casper/vmlinuz autoinstall ds=nocloud;s=/cdrom/nocloud/ --- initrd /casper/initrd } menuentry "Ubuntu Server with the HWE kernel" { set gfxpayload=keep linux /casper/hwe-vmlinuz --- initrd /casper/hwe-initrd } grub_platform if [ "$grub_platform" = "efi" ]; then menuentry 'Boot from next volume' { exit 1 } menuentry 'UEFI Firmware Settings' { fwsetup } else menuentry 'Test memory' { linux16 /boot/memtest86+.bin } fi

Give write permissions to server-iso-extracted/casper/install-sources.yaml:

chmod +w server-iso-extracted/casper/install-sources.yaml

Update server-iso-extracted/casper/install-sources.yaml as follows:

- description:
    en: This version installs a Desktop environment
      where humans are not expected to log in.
  id: desktop 
  locale_support: none
  name:
    en: Ubuntu Desktop
  path: filesystem.squashfs
  size: 568651776
  type: fsimage
  variant: server
  default: true

Reconstruct checksums:

cd server-iso-extracted
chmod +w md5sum.txt
find ./dists ./.disk ./pool ./casper ./boot -type f -print0 | xargs -0 md5sum > md5sum.txt
cd ..

Make new ISO file:

Create a script in your working directory called makeiso.sh with the following contents:

#!/bin/bash
#Parameters found with 'xorriso -indev ubuntu-22.04.3-live-server-amd64.iso -report_el_torito as_mkisofs'
xorriso joliet on -as mkisofs \
-V 'Ubuntu 22.04 Autoinstaller' \
--modification-date="2024111907040900" \
--grub2-mbr --interval:local_fs:0s-15s:zero_mbrpt,zero_gpt:'ubuntu-22.04.5-live-server-amd64.iso' \
--protective-msdos-label \
-partition_cyl_align off \
-partition_offset 16 \
--mbr-force-bootable \
-append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b --interval:local_fs:4162948d-4173019d::'ubuntu-22.04.5-live-server-amd64.iso' \
-appended_part_as_gpt \
-iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 \
-c '/boot.catalog' \
-b '/boot/grub/i386-pc/eltorito.img' \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
--grub2-boot-info \
-eltorito-alt-boot \
-e '--interval:appended_partition_2_start_1040737s_size_10072d:all::' \
-no-emul-boot \
-boot-load-size 10072 \
-o ubuntu-22.04.5-autoinstall-amd64.iso \
server-iso-extracted

Then run the script with source makeiso.sh to make the new ISO.


When I'm all done, the working directory structure looks like this:

$ tree -L 3
.
├── desktop-iso-extracted
│   ├── filesystem.manifest
│   ├── filesystem.manifest-minimal-remove
│   ├── filesystem.manifest-remove
│   ├── filesystem.size
│   ├── filesystem.squashfs
│   ├── filesystem.squashfs.gpg
│   ├── initrd
│   └── vmlinuz
├── makeiso.sh
├── server-iso-extracted
│   ├── boot
│   │   ├── grub
│   │   └── memtest86+.bin
│   ├── boot.catalog
│   ├── casper
│   │   ├── filesystem.manifest
│   │   ├── filesystem.size
│   │   ├── filesystem.squashfs
│   │   ├── hwe-initrd
│   │   ├── hwe-vmlinuz
│   │   ├── initrd
│   │   ├── install-sources.yaml
│   │   ├── ubuntu-server-minimal.manifest
│   │   ├── ubuntu-server-minimal.size
│   │   ├── ubuntu-server-minimal.squashfs
│   │   ├── ubuntu-server-minimal.squashfs.gpg
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.generic-hwe.manifest
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.generic-hwe.size
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.generic-hwe.squashfs
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.generic-hwe.squashfs.gpg
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.generic.manifest
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.generic.size
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.generic.squashfs
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.generic.squashfs.gpg
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.manifest
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.size
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.squashfs
│   │   ├── ubuntu-server-minimal.ubuntu-server.installer.squashfs.gpg
│   │   ├── ubuntu-server-minimal.ubuntu-server.manifest
│   │   ├── ubuntu-server-minimal.ubuntu-server.size
│   │   ├── ubuntu-server-minimal.ubuntu-server.squashfs
│   │   ├── ubuntu-server-minimal.ubuntu-server.squashfs.gpg
│   │   └── vmlinuz
│   ├── dists
│   │   ├── jammy
│   │   ├── stable -> jammy
│   │   └── unstable -> jammy
│   ├── EFI
│   │   └── boot
│   ├── install
│   ├── md5sum.txt
│   ├── nocloud
│   │   ├── meta-data
│   │   └── user-data
│   ├── pool
│   │   ├── main
│   │   └── restricted
│   └── ubuntu -> .
├── tmp
│   ├── filesystem.squashfs
│   ├── modified_filesystem.squashfs
│   └── squashfs-root
│       ├── bin -> usr/bin
│       ├── boot
│       ├── dev
│       ├── etc
│       ├── home
│       ├── lib -> usr/lib
│       ├── lib32 -> usr/lib32
│       ├── lib64 -> usr/lib64
│       ├── libx32 -> usr/libx32
│       ├── media
│       ├── mnt
│       ├── opt
│       ├── proc
│       ├── root
│       ├── run
│       ├── sbin -> usr/sbin
│       ├── snap
│       ├── srv
│       ├── sys
│       ├── tmp
│       ├── usr
│       └── var
├── ubuntu-22.04.5-autoinstall-amd64.iso
├── ubuntu-22.04.5-desktop-amd64.iso
└── ubuntu-22.04.5-live-server-amd64.iso

From here, the ISO can be used to install. With autoinstall configured, you won't be prompted during the installation process.


Update:

Regarding the install-sources.yaml file, you can have multiple entries. By default, autoinstall will select the entry that has the default: true key/value.

Looking at the Subiquity source code repository, documentation for Autoinstall configuration reference manual defines a top-level source key. Under source, two more keys can be defined, search-drivers and id. By default, search-drivers is true and id is blank, which means that autoinstall will search the install-sources.yaml file for an entry with a default: true key/value and will then choose this as the source to install. Alternatively, you can define id in your autoinstall config, which tells autoinstall to search install-sources.yaml for an entry with a matching id. It will then install that source.

source

  • type: mapping, see below
  • default: see below
  • can be interactive: true

search_drivers

  • type: boolean
  • default: true (mostly, see below)

Whether the installer searches for available third-party drivers. When set to false, it disables the drivers :ref:screen and section<ai-drivers>.

The default is true for most installations, and false when a "core boot" or "enhanced secure boot" method is selected (where third-party drivers cannot be currently installed).

id

  • type: string
  • default: the default value as listed in install-sources

Identifier of the source to install (e.g., ubuntu-server-minimal). The correct ID to use is specific to a given installation ISO. As this ID may change over time, the canonical place to look for this information is the installation ISO itself, in the casper/install-sources.yaml file where the value to use is the id.

Source examples:

autoinstall:
  # default behaviour
  source:
    search_drivers: true
    id: <the installation source marked as default in install-sources.yaml>

autoinstall:

on the Ubuntu Server ISO, install with the minimal source

source: id: ubuntu-server-minimal

autoinstall:

on the Ubuntu Desktop ISO, install with the standard source

source: id: ubuntu-desktop


As for defining the size of the squashfs in install-sources.yaml, the Subiquity source code states that it's used in determining the amount of space needed when formatting the disk. So yes, the more accurate this is the better.

# Factors for suggested minimum install size:
# 1) Source minimum - The minimum reported as part of source selection.  This
#    is absolute bare minimum information to get bits on the disk and doesn’t
#    factor in filesystem overhead.   Obtained from the size value of the
#    chosen source as found at /casper/install-sources.yaml.
# 2) Room for boot - we employ a scaling system to help select the recommended
#    size of a dedicated /boot and/or efi system partition (see above).  If
#    /boot is not actually a separate partition, this space needs to be
#    accounted for as part of the planned rootfs size.
# 3) room for esp - similar to boot.  Included in all calculations, even if
#    we're not UEFI boot.
# 4) Room to grow - while meaningful work can sometimes be possible on a full
#    disk, it’s not the sort of thing to suggest in a guided install.
#    Suggest for room to grow max(2GiB, 50% of source minimum).
def calculate_suggested_install_min(source_min: int, part_align: int = MiB) -> int:
    room_for_boot = bootfs_scale.minimum
    room_for_esp = uefi_scale.minimum
    room_to_grow = max(2 * GiB, math.ceil(0.5 * source_min))
    total = source_min + room_for_boot + room_for_esp + room_to_grow
    return align_up(total, part_align)
mpboden
  • 3,046