How to create a custom Ubuntu image (snap/2.9/CLI)

MAAS supports deploying custom DD or TGZ images. Canonical provides both lp:maas-image-builder and gh:canonical/packer-maas to support creating custom images; however, these tools do not currently support Ubuntu. Instead Canonical suggests customising Ubuntu using cloud-init user_data or Curtin preseed data.

Why customised Ubuntu deployments aren’t supported

When the MAAS stream is generated by lp:maas-images it starts by downloading the base SquashFS rootfs from that is used for all clouds. The SquashFS does not contain a kernel so lp:maas-images mounts the SquashFS with an overlay and chroots in. It then installs a kernel and extra initramfs scripts from the cloud-initramfs-rooturl and cloud-initramfs-copymods packages to allow network booting. Once everything is installed the kernel and newly generated initramfs are pulled out of the overlay and everything is unmounted. lp:maas-images provides the unmodified SquashFS, installed kernel, and generated initramfs as separate files on

MAAS uses the kernel, initramfs, and SquashFS to perform network booting which allows commissioning, testing, and deployments. When deploying Ubuntu MAAS uses the same version of Ubuntu to network boot into and perform the deployment. This ensures there are no compatibility issues. Other operating systems use the Ubuntu version selected for the ephemeral environment for deployment.

Currently MAAS only allows custom images to be loaded as a single TGZ or DD.GZ, there is no way to upload a kernel, initrd, and rootfs as separate files. Even if MAAS was modified to allow the kernel, initrd, and rootfs as separate files MAAS requires cloud-initramfs-copymods and cloud-initramfs-rooturl to be included in the initrd. It is difficult for MAAS to detect if these scripts are missing and even harder for users to debug if they are missing.

Warnings on creating a custom Ubuntu image

  1. Custom images are always deployed with the ephemeral operating system. This can cause hard to debug errors. For example CentOS 6 can only be deployed by Ubuntu Xenial due to advances in the ext4 filesystem.
  2. MAAS will still install and configure the GA kernel. If your custom image contains a kernel it most likely won’t be used. MAAS will not allow you to select which kernel(GA, HWE, lowlatency, etc) when deploying a custom Ubuntu image.
  3. All GNU/Linux custom images should be created as a TGZ to enable storage customisation. When a DD.GZ is used MAAS/Curtin simply writes the file to the filesystem.

How to create a custom Ubuntu image for MAAS

Note: LXD may prevent device files from being created when extracting the rootfs, it is suggested to do this on metal or on a VM:

  1. Download the rootfs from

  2. Create a work directory
    mkdir /tmp/work

  3. Extract the rootfs
    cd /tmp/work
    sudo tar xf ../focal-server-cloudimg-amd64-root.tar.xz


    unsquashfs ../focal-server-cloudimg-amd64-root.squash

  4. Setup chroot
    sudo mount -o bind /proc /tmp/work/proc
    sudo mount -o bind /dev /tmp/work/dev
    sudo mount -o bind /sys /tmp/work/sys
    sudo mv /tmp/work/etc/resolv.conf /tmp/work/etc/resolv.conf.bak
    sudo cp /etc/resolv.conf /tmp/work/etc/

  5. Chroot in
    sudo chroot /tmp/work /bin/bash

  6. Customise image
    apt update
    apt dist-upgrade
    apt install emacs-nox jq tree
    apt autoremove

  7. Exit chroot and unmount binds
    sudo umount /tmp/work/proc
    sudo umount /tmp/work/dev
    sudo umount /tmp/work/sys
    sudo mv /tmp/work/etc/resolv.conf.bak /tmp/work/etc/resolv.conf

  8. Create TGZ
    sudo tar -czf ~/focal-custom.tgz -C /tmp/work .

  9. Upload it to MAAS
    Note: Ubuntu release names and versions are reserved
    maas $PROFILE boot-resources create name='custom/focal-custom' title='Ubuntu 20.04 Custom Image' architecture='amd64/generic' filetype='tgz' content@=~/focal-custom.tgz

  10. Configure and deploy as normal

for the 2.9 snap, step 9 above returns with a usage error.

The help text suggests adding sha256 and size also for the file upload, but also says:

 :param content: Optional.  Image content. Note: this is
not a normal parameter, but an ``application/octet-stream`` file
:type content: String

It would be good to get some explanation as to what that means in practice.

When I try to perform the upload using:

maas $PROFILE boot-resources create name='custome/somename' title='sometitle' architecture='amd64/generic' filetype='tgz' sha256=$checksum size=$size content@=mycustomimage.tgz

I still get a usage error.

Can we clarify the docs here?

assuming this is literal, you cannot name it custome/somename it has to be custom/somename

I see this error come back as:

{"name": ["Unsupport operating system custome, supported operating systems: ['ubuntu', 'ubuntu-core', 'bootloader', 'centos', 'rhel', 'custom', 'windows', 'suse', 'caringo', 'esxi']"]}

which seems quite clear?

Unfortunately the custome is a typo in my post here, likely made as I was anonymizing the text.

The error I saw was the same usage error as if I had typed a totally unsupported command, or missed off some options.

@xavpaice, step 9 does have an error (now fixed in the doc, hopefully). since you did all the other steps in /tmp, you have to change content@=mycustomimage.tgz to be /tmp/mycustomimage.tgz. maybe you’ve already done that?

Thanks so much for the help in chat. The problem turned out to be the maas profile being used wasn’t set up correctly, once that got fixed, the upload worked fine.

So, the doc changes (for 2.9 snap at least) is just that we should be adding the size and sha256 as required params.

1 Like

and fixing the inconsistent paths, which i think has already been done.