How to install apt packages offline with Ubuntu 22.04 Server Autoinstall

If you are making an autoinstall ISO based off Ubuntu server, you may be wondering how you can install packages from apt in an offline way. Unfortunately, the Ubuntu documentation for autoinstall doesn’t explain how to do this. This method works for apt but does not work for snaps. Snaps are garbage anyways. Thank you to Puget Systems for their great documentation on customizing isos. This code is provided as is with no warranty, it may brick your system or may not work as intended, you have been warned.

Here’s how (note there are some files you must create first, their contents is at the end of this list of commands):

  1. Place your iso file in a directory, cd in terminal into that directory
  2. mkdir isotemp
  3. cd isotemp
  4. mkdir source-files
  5. 7z -y x "yourisofilelocation.iso" -osource-files # extracts the iso
  6. cd source-files
  7. mv '[BOOT]' ../BOOT # we don’t need these in the iso as they will be regenerated
  8. cd ..
  9. cd .. # get back to root dir
  10. mkdir server
  11. touch server/meta-data # create blank meta-data file for autoinstall it will not work if this file doesn’t exist
  12. cp /path/to/your/user-data-yaml-file server/user-data
  13. cd ..
  14. sudo unsquashfs source-files/casper/ubuntu-server-minimal.squashfs # unsquash filesystem used to create target install
  15. mv squashfs-root edit
  16. sudo mount -o bind /run/ edit/run #make chroot
  17. sudo mkdir edit/dev
  18. sudo mount --bind /dev/ edit/dev
  19. sudo touch edit/chroot_script.sh
  20. sudo chmod 777 edit/chroot_script.sh
  21. sudo mkdir -p edit/opt/yourcustomfolder
  22. sudo cp -rl "/dir/with/files/you/want/to/copy/." edit/opt/yourcustomfolder/ # this folder should contain any other files you need to copy to the installed system, you can skip this if you have none
  23. chmod -R +r edit/opt/yourcustomfolder
  24. cp ../chroot_script.sh edit/chroot_script.sh # this is the script that will be run within the chroot and download stuff via apt
  25. chmod +x edit/chroot_script.sh
  26. sudo chroot edit /bin/bash -c "su - -c /chroot_script.sh” # run the chroot script
  27. sudo umount edit/run # close the chroot
  28. sudo umount edit/dev
  29. sudo rm -r edit/dev
  30. sudo chmod +w source-files/casper/ubuntu-server-minimal.manifest
  31. sudo chroot edit dpkg-query -W --showformat='${Package} ${Version}\n' > source-files/casper/ubuntu-server-minimal.manifest
  32. sudo rm source-files/casper/ubuntu-server-minimal.squashfs
  33. sudo rm source-files/casper/ubuntu-server-minimal.squashfs.gpg
  34. sudo mksquashfs edit source-files/casper/ubuntu-server-minimal.squashfs -comp xz #re-squash filesystem
  35. sudo printf "$(sudo du -sx --block-size=1 edit | cut -f1)" > source-files/casper/ubuntu-server-minimal.size 2>/dev/null # re-calculate squash size
  36. sudo rm -r edit
  37. cd source-files
  38. Run the command below to make the iso file (output.iso, be sure to edit to correct path). Note the period on the final line!

xorriso -as mkisofs -r -iso-level 3 \
  -V 'Ubuntu 22.04 LTS AUTO (EFIBIOS)' \
  -o /path/to/output.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 --grub2-boot-info \
  -eltorito-alt-boot \
  -e '--interval:appended_partition_2:::' \
  -no-emul-boot \
.
Contents of chroot_script.sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devpts none /dev/pts
apt-get -y update
apt-get -yyqq install thunderbird # you can install packages here if you want
apt-get -yyqq install --download-only package1 package2 # or simply download them for installation by autoinstall
umount /proc
umount /sys
umount /dev/pts

Contents of user-data-yaml-file:
#cloud-config
autoinstall:
  version: 1
  late-commands:
    # copy downloaded apt files to new system
    - rm -r /target/var/cache/apt
    - cp -r /media/minimal/var/cache/apt /target/var/cache/
    - curtin in-target --target /target -- apt-get -yy install package1 package2 package3