Here is what I did on Ubuntu 20.04 live server (focal).
The goal is to install Ubuntu without network connection but with some packages not available in the official ISO.
- Create a custom ISO file based on the official
ubuntu-20.04.6-live-server-amd64.iso
- Create a custom GPG key to sign
Release.gpg and install it into filesystem.squashfs
- Add the
*.deb files to the extracted-iso/pool/extra/ directory
Missing *.deb were downloaded from a running target connected to internet and default APT sources to Ubuntu's servers. (Yeah, this is not optimal but couldn't do better). More info here
$ sudo apt update
$ apt-get --print-uris --yes install <PACKAGE_NAMES> | grep ^\' > package-list.txt
$ wget $(cat package-list.txt | cut -d "'" -f 2) -N -P extracted-iso/pool/extra
- Add a custom component named
extra with all the metadata generated by apt-ftparchive , it gives something like this :
extracted-iso/dists/
├── focal
│ ├── Release
│ ├── Release.gpg
│ ├── extra
│ │ └── binary-amd64
│ │ ├── Packages
│ │ ├── Packages.gz
│ │ └── Release
│ ├── main
│ │ ├── binary-amd64
│ │ │ ├── Packages
│ │ │ ├── Packages.gz
│ │ │ └── Release
│ │ ├── binary-i386
│ │ │ ├── Packages
│ │ │ ├── Packages.gz
│ │ │ └── Release
│ │ └── source
│ └── restricted
│ ├── binary-amd64
│ │ ├── Packages
│ │ ├── Packages.gz
│ │ └── Release
│ ├── binary-i386
│ │ ├── Packages
│ │ ├── Packages.gz
│ │ └── Release
│ └── source
├── stable -> focal
└── unstable -> focal
- Configure
autoinstall.yaml to launch a custom script named late-commands.sh (Optionnal but cleaner to me)
#cloud-config
# File : /cdrom/<my_custom_dir_in_iso>/user-data (i.e. autoinstall.yaml)
autoinstall:
# ...
# Launch a custom script which regroup all late-commands
late-commands:
- /cdrom/<my_custom_dir_in_iso>/late-commands.sh
- Install your packages with
apt from the ISO mounted on /cdrom
#!/bin/bash
# File : late-commands.sh
# Note "/cdrom" is not mounted anymore at the step of late-commands !
Mount back the cdrom (hopping "/sdb1" is a constant)
curtin in-target -- mkdir -p /cdrom
curtin in-target -- mount -o ro /dev/sdb1 /cdrom
Add a new source for APT pointing to the custom *.deb repo on /cdrom
echo "deb [check-date=no] file:///cdrom focal main restricted extra" > /target/etc/apt/sources.list.d/local_cdrom.list
Update packages list from "local_cdrom.list" uris
curtin in-target -- apt update
Install custom packages and dependencies
curtin in-target -- apt install -y <PACKAGE_NAMES>
Clean APT source and umount cdrom
rm -rf /target/etc/apt/sources.list.d/local_cdrom.list
curtin in-target -- umount /cdrom
curtin in-target -- rm -rf /cdrom
Why the official way did not work ?
The official way of installing packages during an autoinstall procedure is to specify them inside packages:.
In the special case where the repo is local to the ISO (aka offline install), the installer failed during the curtin step where it launches apt update. At this point, /cdrom is not mounted on the target filesystem and APT cannot retreives Release and Packages files.
#cloud-config
autoinstall:
# ...
apt:
# THIS DO NOT WORK !
# Adding a custom /etc/apt/sources.list.d/local_cdrom.list which appends the
# component "extra" to the existing "main" and "restricted" in /etc/apt/sources.list
sources:
local_cdrom.list:
source: "deb [check-date=no] file:///cdrom focal main restricted extra"
Install package from "/cdrom > focal > extra"
packages:
- <PACKAGE_NAMES>
PS: I didn't use a solution based on apt-offline because you need the package to be installed in the first place on the target