UBUNTU-IMAGE(1)

๐Ÿ“ฆ version 2.2+22.04ubuntu3.22.04.2, in ubuntu jammy

๐Ÿ’– UBUNTU-IMAGE ๐Ÿ’–

okay bestie, gather round, because this is the manual page for ubuntu-image (that's the ubuntu-image command, section 1 of the manual, if you're keeping track) and she is busy in the best way. she makes bootable disk images. plural. a whole variety. let's get into it ๐Ÿซถ


โœจ NAME โœจ

ubuntu-image โ€” generate a bootable disk image. that's the whole job. that's her whole thing. and she eats every time ๐Ÿ’…


๐ŸŒธ SYNOPSIS ๐ŸŒธ

ubuntu-image snap [options] model.assertion

ubuntu-image classic [options] GADGET_TREE_URI

two modes, two vibes. pick your fighter โœจ


๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

so ubuntu-image is a program for generating a variety of bootable disk images. currently she supports building snap based and classic preinstalled Ubuntu images. two flavors, both iconic ๐Ÿ‘‘

Snap-based images are built from a model assertion, which is a YAML file describing a particular combination of core, kernel, and gadget snaps, along with other declarations, signed with a digital signature asserting its authenticity. like, the signature is the whole point โ€” it's the vibe check that proves this file is legit. the assets defined in the model assertion uniquely describe the device for which the image is built. no cap.

as part of the model assertion, a gadget snap is specified. the gadget contains a gadget.yaml file which contains the exact description of the disk image's contents, in YAML format. the gadget.yaml file describes such things as the names of all the volumes to be produced [1], the structures [2] within the volume, whether the volume contains a bootloader and if so what kind of bootloader, etc. it's giving blueprint energy ๐Ÿ“

heads up bestie: ubuntu-image communicates with the snap store using the snap prepare-image subcommand. the model assertion file is passed to snap prepare-image which handles downloading the appropriate gadget and any extra snaps. so if you're confused about the download part, that's not her, that's snapd. see that command's documentation for additional details ๐Ÿ’–

Classic images are built from a local gadget tree path. the gadget tree is nothing more than a primed gadget snap, containing a gadget.yaml file in the meta directory and all the necessary bootloader gadget bits built. for instance a gadget tree can be easily prepared by fetching a specially tailored gadget snap source and running snapcraft prime on it, with the resulting tree ending up in the prime/ directory. lowkey elegant.

the actual rootfs for a classic image is created by live-build with arguments passed as per the optional arguments to ubuntu-image. the livecd-rootfs configuration from the host system is used.


๐Ÿ’… OPTIONS ๐Ÿ’…

๐ŸŒˆ general ones ๐ŸŒˆ

  • -h, --help โ€” show the program's message and exit. the "help me bestie" button, always there for you ๐Ÿซถ
  • --version โ€” show the program's version number and exit. for when you need to know which ubuntu-image you're talking to.

๐ŸŽ€ Snap command options ๐ŸŽ€

these are the options for defining the contents of snap-based images. can only be used when the ubuntu-image snap command is used. like, they will not work in classic mode, she's not flexible about that.

  • model_assertion โ€” path to the model assertion file. this positional argument must be given for this mode of operation. no assertion, no image, periodt.
  • --cloud-init USER-DATA-FILE โ€” cloud-config data to be copied to the image. yes, it gets copied straight in, so make it good โ˜๏ธ
  • --disable-console-conf โ€” disable console-conf on the resulting image. bye bye console-conf, we hardly knew ye ๐Ÿ™ˆ
  • --factory-image โ€” hint that the image is meant to boot in a device factory. it's just a hint, not a command, she's not bossy like that.
  • --validation=<ignore|enforce> โ€” control whether validations should be ignored or enforced. two choices, no in-between, choose wisely.

๐Ÿ‘‘ Classic command options ๐Ÿ‘‘

these are the options for defining the contents of classic preinstalled Ubuntu images. can only be used when the ubuntu-image classic command is used. again: mode-specific, she does not mix.

  • GADGET_TREE_URI โ€” an URI to the gadget tree to be used to build the image. this positional argument must be given for this mode of operation. must be a local path. like, don't go putting an https:// in there, bestie, she will not know what to do with it ๐Ÿ˜ญ
  • -p PROJECT, --project PROJECT โ€” project name to be passed on to livecd-rootfs. mutually exclusive with --filesystem. pick one, you can't have both, that's the rule ๐Ÿ’€
  • -f FILESYSTEM, --filesystem FILESYSTEM โ€” unpacked Ubuntu filesystem to be copied to the system partition. mutually exclusive with --project. again: one or the other, not both.
  • -s SUITE, --suite SUITE โ€” distribution name to be passed on to livecd-rootfs.
  • -a CPU-ARCHITECTURE, --arch CPU-ARCHITECTURE โ€” CPU architecture to be passed on to livecd-rootfs. default value is the architecture of the host. so if you don't say anything, she just assumes you mean this machine, which is honestly so real of her โœจ
  • --subproject SUBPROJECT โ€” sub-project name to be passed on livecd-rootfs.
  • --subarch SUBARCH โ€” sub-architecture to be passed on to livecd-rootfs.
  • --with-proposed โ€” defines if the image should be built with -proposed enabled. this is passed through to livecd-rootfs. careful with this one, -proposed is exactly what it sounds like ๐Ÿ“
  • --extra-ppas EXTRA_PPAS โ€” extra ppas to install. this is passed through to livecd-rootfs.

๐Ÿ’œ Common options ๐Ÿ’œ

there are two general operational modes to ubuntu-image. the usual mode is to run the script giving the required model assertion file as a required positional argument, generating a disk image file. these options are useful in this mode of operation. this is the everyday, main-character mode ๐ŸŒŸ

the second mode of operation is provided for debugging and testing purposes. it allows you to run the internal state machine step by step, and is described in more detail below. this is the "peek behind the curtain" mode, and honestly? kind of fascinating.

  • -d, --debug โ€” enable debugging output. turn it on when you want to see everything, bestie. she will tell you all her business ๐Ÿ—ฃ๏ธ
  • -O DIRECTORY, --output-dir DIRECTORY โ€” write generated disk image files to this directory. the files will be named after the gadget.yaml volume names, with .img suffix appended. if not given, the current working directory is used. this option replaces, and cannot be used with, the deprecated --output option. so if you were still using --output... ma'am. it's deprecated. move on ๐Ÿ’…
  • -i SIZE, --image-size SIZE โ€” the size of the generated disk image files. if this size is smaller than the minimum calculated size of the volume, a warning will be issued and --image-size will be ignored. the value is the size in bytes, with allowable suffixes 'M' for MiB and 'G' for GiB.

an extended syntax is supported for gadget.yaml files which specify multiple volumes (i.e. disk images). in that case, a single SIZE argument will be used for all the defined volumes, with the same rules for ignoring values which are too small. you can specify the image size for a single volume using an indexing prefix on the SIZE parameter, where the index is either a volume name or an integer index starting at zero. for example, to set the image size only on the second volume, which might be called sdcard in the gadget.yaml, you could use: --image-size 1:8G since the 1-th index names the second volume (volumes are 0-indexed). or you could use --image-size sdcard:8G.

you can also specify multiple volume sizes by separating them with commas, and you can mix and match integer indexes and volume name indexes. thus, if the gadget.yaml named three volumes, and you wanted to set all three to different sizes, you could use --image-size 0:2G,sdcard:8G,eMMC:4G. comma-separated chaos, but make it organized ๐ŸŽ€

in the case of ambiguities, the size hint is ignored and the calculated size for the volume will be used instead. she's not gonna guess. she's gonna do the math herself.

  • --image-file-list FILENAME โ€” print to FILENAME, a list of the file system paths to all the disk images created by the command, if any. great for scripts. great for "where did my images go" moments.
  • --hooks-directory DIRECTORY โ€” directories in which scripts for build-time hooks will be located. this flag must be specified once for each hook directory. ubuntu-image will look for hooks in hooks_directory/name_of_hooks_step.d and a script with the name hooks_directory/name_of_hooks_step. currently the only supported hooks step is populate_rootfs_contents. note that: the ONLY supported hooks step. don't go inventing new ones, she won't call them ๐Ÿช
  • --disk-info DISK-INFO-CONTENTS โ€” file to be used as .disk/info on the image's rootfs. this file can contain useful information about the target image, like image identification data, system name, build timestamp etc. basically her little name tag for the image ๐Ÿท๏ธ
  • --snap SNAP โ€” install an extra snap. this is passed through to snap prepare-image. the snap argument can include additional information about the channel and/or risk with the following syntax: <snap>=<channel|risk>. so like, specify the snap and where to get it, in one little bundle.
  • -c CHANNEL, --channel CHANNEL โ€” the snap channel to use.
  • --sector-size SIZE โ€” when creating the disk image file, use the given sector size. this can be either 512 or 4096 (4k sector size), defaulting to 512. two options. just two. that's it. don't get creative.

๐Ÿ”ฎ State machine options ๐Ÿ”ฎ

CAUTION! the options described here are primarily for debugging and testing purposes and should not be considered part of the stable, public API. state machine step numbers and names can change between releases. so like, if you build your whole workflow on these, that's on you bestie ๐Ÿ’€

ubuntu-image internally runs a state machine to create the disk image. these are some options for controlling this state machine. other than --workdir, these options are mutually exclusive. when --until or --thru is given, the state machine can be resumed later with --resume, but --workdir must be given in that case since the state is saved in a .ubuntu-image.pck file in the working directory.

  • -w DIRECTORY, --workdir DIRECTORY โ€” the working directory in which to download and unpack all the source files for the image. this directory can exist or not, and it is not removed after this program exits. if not given, a temporary working directory is used instead, which is deleted after this program exits. use --workdir if you want to be able to resume a partial state machine run. as an added bonus, the gadget.yaml file is copied to the working directory after it's downloaded ๐ŸŽ
  • -u STEP, --until STEP โ€” run the state machine until the given STEP, non-inclusively. STEP is the name of a state machine method. the list of all steps can be found in the STEPS section of this document. note: until = stop before it. non-inclusive. she stops just shy of the finish line ๐Ÿƒโ€โ™€๏ธ
  • -t STEP, --thru STEP โ€” run the state machine through the given STEP, inclusively. STEP is the name of a state machine method. the list of all steps can be found in the STEPS section of this document. note: thru = go through it, inclusive, all the way in ๐Ÿ’–
  • -r, --resume โ€” continue the state machine from the previously saved state. it is an error if there is no previous state. like, you can't resume what was never started, bestie ๐Ÿ˜ญ

๐ŸŒˆ FILES ๐ŸŒˆ

โœจ ENVIRONMENT โœจ

okay bestie, ubuntu-image is nosey (affectionate) and it reads these environment variables. here's the whole roster:

  • UBUNTU_IMAGE_PRESERVE_UNPACK โ€” set this and it names a directory where a pristine copy of the unpacked gadget contents gets stashed. important: the directory must already exist, and an unpack directory gets created under this directory. basically: the full contents of the <workdir>/unpack directory โ€” as it looks after the snap prepare-image subcommand has run โ€” get copied here. ๐Ÿ’–
  • UBUNTU_IMAGE_LIVECD_ROOTFS_AUTO_PATH โ€” ubuntu-image uses livecd-rootfs configuration files for its live-build runs. if this is set, it pulls the config files from the path you picked for its auto configuration. if it's not set, it goes and tries to localize livecd-rootfs by making a call to dpkg. ๐ŸŽ€
  • UBUNTU_IMAGE_QEMU_USER_STATIC_PATH โ€” when you're cross-compiling a classic image for a different architecture, ubuntu-image tries to use the qemu-user-static emulator with live-build. set this and it uses the path you chose for the cross-compilation. don't set it and it'll go hunting for a matching emulator binary in your current $PATH. ๐Ÿ”
also there are a few other environment variables floating around, but those are for building and testing only. not your business (respectfully). ๐Ÿ’…

๐Ÿช HOOKS ๐Ÿช

so during an image build, at certain stages, you get to run custom scripts that modify the image contents or otherwise mess with the process itself. main character energy, honestly.

whenever a hook fires, the directories listed in the --hooks-directory parameter get scanned for matching scripts. and yes, there can be multiple scripts for a specific hook. here's the order, bestie:

  1. HookManager first looks for executable files in <hookdir>/<name-of-the-hook>.d and runs them in alphanumerical order. ๐Ÿ“‹
  2. then, finally, the <hookdir>/<name-of-the-hook> file gets executed โ€” if it exists.

hook scripts can also get extra data passed to them through environment variables, depending on which hook is firing. cute, right?

here's what's currently supported:

post-populate-rootfs

runs after the rootfs directory has been populated, so you can custom-modify its contents. added in version 1.2. ๐ŸŽ‰

environment variables present:

  • UBUNTU_IMAGE_HOOK_ROOTFS โ€” the absolute path to the rootfs contents. ๐Ÿ’–

๐Ÿ‘‘ STEPS ๐Ÿ‘‘

the names of steps you can use with --until and --thru for each image type are listed below. slay.

Classic image steps

  1. make_temporary_directories
  2. prepare_gadget_tree
  3. run_live_build
  4. load_gadget_yaml
  5. populate_rootfs_contents
  6. populate_rootfs_contents_hooks
  7. generate_disk_info
  8. calculate_rootfs_size
  9. populate_bootfs_contents
  10. populate_prepare_partitions
  11. make_disk
  12. generate_manifest
  13. finish

Snap image steps

  1. make_temporary_directories
  2. prepare_image
  3. load_gadget_yaml
  4. populate_rootfs_contents
  5. populate_rootfs_contents_hooks
  6. generate_disk_info
  7. calculate_rootfs_size
  8. populate_bootfs_contents
  9. populate_prepare_partitions
  10. make_disk
  11. generate_manifest
  12. finish

๐Ÿ“ NOTES ๐Ÿ“

sometimes, for various reasons, ubuntu-image does specific little workarounds, and you might be sitting there like "whyโ€ฆ". so here's the reasoning, bestie.

Classic swapfile manual unsparsing

when building a classic image, if ubuntu-image notices a /swapfile hanging out on the image's rootfs, it will proactively try to unsparse it.

why tho? because ubuntu-image assumes that /swapfile file is gonna be used as a swapfile on the target system. and due to undocumented behavior of mkfs.ext4 -d, large empty files get automatically converted into sparse files during filesystem population. which, lowkey, makes such files completely unusable as swapfiles. ๐Ÿ˜ญ

so just in case, ubuntu-image does an in-place dd call of the hard-coded path swapfile to make sure it's not sparse anymore. problem solved, periodt. ๐Ÿ’…


๐ŸŒˆ SEE ALSO ๐ŸŒˆ


๐Ÿฆ‹ FOOTNOTES ๐Ÿฆ‹

  • [1] Volumes are roughly analogous to disk images.
  • [2] Structures define the layout of the volume, including partitions, Master Boot Records, or any other relevant content.

๐Ÿ’œ AUTHOR ๐Ÿ’œ

  • Barry Warsaw <barry@ubuntu.com>
  • ลukasz 'sil2100' Zemczak <lukasz.zemczak@ubuntu.com>
  • William 'jawn-smith' Wilson <william.wilson@canonical.com>

2016-2021 Canonical Ltd.


and that's the whole page, bestie!! this one is version 2.0, dated 2021-10-21, and it lives under UBUNTU-IMAGE(1). go build something iconic with it ๐Ÿซถโœจ๐Ÿ‘‘

๐Ÿ“ฆ where this page comes from