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,695
  • 134
  • 305
  • 337
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,134
  • Thank you for taking the time to answer my question. I will try executing the steps you suggested and let you know if it works! – Rajaram Nov 14 '24 at 05:05
  • Is it okay to keep the ubuntu-server-minimal.squashfsand ubuntu-server-minimal.ubuntu-server.squashfs entries by removing the default: true setting in the install-sources.yaml file.

    Also when I calculate the size for filesystem.squashfs using the stat command, I get the following value:

    2644025344
    

    However, I’m not sure if I should proceed with the default values or if I should adjust them based on the stat output. @mpboden Could you please advise on this?"

    – Rajaram Nov 15 '24 at 05:48
  • @Rajaram I've updated my answer to address your questions – mpboden Nov 15 '24 at 07:45
  • Following the above instructions, I tested the ISO multiple times. While the installation completes successfully, the system does not boot afterward and instead directly runs Memtest. I’m not sure what I might be doing wrong. Could you please advise? – Rajaram Nov 19 '24 at 04:58
  • @Rajaram I'm not quite sure why it's not working for you. As I follow these steps myslef, I'm able to create a new ISO that boots in BIOS mode within VirtualBox. It wasn't working in EFI mode, so I've updated my post to show a new command to create the ISO that'll work in either BIOS or EFI mode within VirtualBox. I suggest you start from the beginning and go step-by-step once again. P.S. I'm using VirtualBox 7.0.22 on an Ubuntu 22.04 host. – mpboden Nov 19 '24 at 07:28
  • I managed to get it done by extracting the server ISO and installing the desktop package within the chroot environment. However, I'm not sure what I'm doing wrong when attempting to add the squashfs filesystem from the Ubuntu Desktop ISO to the Server ISO. Thanks a lot for your help, @mpboden! – Rajaram Dec 05 '24 at 12:30