This repository provides Packer templates, scripts, and configuration to build custom operating system images for MAAS.
Use these templates if you need:
- Custom Ubuntu images with pre-installed packages, security hardening, or organization-specific tweaks.
- Non-Ubuntu images (RHEL, CentOS, SLES, Windows, ESXi, etc.) that MAAS does not provide out-of-the-box.
- A repeatable, automated build process for images you can upload into MAAS.
⚠️ If you only need stock Ubuntu images, see the How to manage images guide instead.
- Consistency: Standardize environments across your MAAS deployments.
- Control: Add, remove, or patch software before deployment.
- Compliance: Ensure security and audit requirements are met.
- Coverage: Deploy non-Ubuntu operating systems through MAAS.
MAAS relies on these images when commissioning, deploying, and testing machines. Custom images let you tailor exactly what gets deployed.
Before building an image, prepare a build environment:
- An Ubuntu host or VM with:
- 4 CPU cores
- 8 GB RAM
- 25 GB free storage
- Hardware-assisted virtualization enabled
- Packer installed
- (Optional) QEMU with GUI if you want to see builds interactively
Verify Packer is installed:
packer versionFollow these steps to build and make an image available in MAAS.
git clone https://github.com/canonical/packer-maas.git
cd packer-maasEach supported operating system has its own directory with:
- One or more HCL2 templates
- A
scripts/directory with helper scripts - An
http/directory with auto-configuration files - A
README.mdexplaining OS-specific details - A
Makefilefor convenience
See the Existing templates table below for the full list.
Run Packer from within the template directory. Example for Ubuntu:
cd ubuntu
packer build ubuntu.pkr.hclFor debugging, use:
PACKER_LOG=1 packer build ubuntu.pkr.hclIf you want to view the VM build process:
- Remove
"headless": truefrom the template - Or connect via VNC using the IP/port shown during build
After building, upload the image to your MAAS region controller:
maas $PROFILE boot-resources create name='custom/ubuntu-24.04' title='Ubuntu 24.04 Custom' architecture='amd64/generic' filetype='tgz' content@=ubuntu-24.04-custom.tgzℹ️ Commands vary slightly by OS — see the template’s
README.mdfor exact syntax.
Check that the image is available:
maas $PROFILE boot-resources read | grep customThen deploy a test machine with the new image and confirm you can log in.
Here’s a complete example for building and uploading a custom Ubuntu image.
- Install dependencies
sudo apt update
sudo apt install packer qemu-utils qemu-system ovmf cloud-image-utils- Clone the Packer MAAS repository
git clone https://github.com/canonical/packer-maas.git
cd packer-maas/ubuntu- Build an image
Use the included Makefile to build an Ubuntu LVM image:
make custom-ubuntu-lvm.dd.gzThis may take a few minutes. Packer will boot the image in headless mode and attempt repeated SSH handshakes until provisioning succeeds.
- Upload the image to MAAS
maas admin boot-resources create \
name='custom/ubuntu-raw' \
title='Ubuntu Custom RAW' \
architecture='amd64/generic' \
filetype='ddgz' \
content@=custom-ubuntu-lvm.dd.gz- Verify deployment
Deploy the image to a test machine:
maas admin machines read | jq -r '(["HOSTNAME","SYSID","STATUS","OS","DISTRO"]),
(.[] | [.hostname, .system_id, .status_name, .osystem, .distro_series]) | @tsv' | column -tYou should see your custom image listed under OS = custom.
Log in with the default Ubuntu username:
ssh ubuntu@<machine-ip>Every OS template can be adjusted to include your own configuration. Common options include:
- Adding extra packages in the provisioner step
- Including custom cloud-init or preseed files
- Adjusting the Packer
boot_commandto change installation behavior - Changing image names (
name,title) to avoid cache conflicts
Refer to the README.md inside each OS directory for supported parameters.
| OS | Maturity Level | Architecture | MAAS Version |
|---|---|---|---|
| Azure Local (HCI) | Beta | x86_64 | >= 3.3 |
| AlmaLinux 8 | Beta | x86_64 / aarch64 | >= 3.3 |
| AlmaLinux 9 | Beta | x86_64 / aarch64 | >= 3.3 |
| AlmaLinux 10 | Beta | x86_64 / aarch64 | >= 3.3 |
| AzureLinux 2.0 | Beta | x86_64 | >= 3.3 |
| CentOS 6 | EOL | x86_64 | >= 1.6 |
| CentOS 7 | EOL | x86_64 | >= 2.3 |
| CentOS 8 | EOL | x86_64 | >= 2.7 |
| CentOS 8 Stream | Beta | x86_64 | >= 3.2 |
| CentOS 9 Stream | Beta | x86_64 | >= 3.2 |
| Debian 10 | EOL | x86_64 / aarch64 | >= 3.2 |
| Debian 11 | Beta | x86_64 / aarch64 | >= 3.2 |
| Debian 12 | Beta | x86_64 / aarch64 | >= 3.2 |
| Debian 13 | Beta | x86_64 / aarch64 | >= 3.2 |
| Fedora Server 41 | Beta | x86_64 / aarch64 | >= 3.2 |
| Fedora Server 42 | Beta | x86_64 / aarch64 | >= 3.2 |
| OL8 | Alpha | x86_64 | >= 3.5 |
| OL9 | Alpha | x86_64 | >= 3.5 |
| RHEL 7 | EOL | x86_64 | >= 2.3 |
| RHEL 8 | Stable | x86_64 / aarch64 | >= 2.7 |
| RHEL 9 | Beta | x86_64 / aarch64 | >= 3.3 |
| RHEL 10 | Beta | x86_64 / aarch64 | >= 3.3 |
| Rocky 8 | Beta | x86_64 / aarch64 | >= 3.3 |
| Rocky 9 | Beta | x86_64 / aarch64 | >= 3.3 |
| SLES 12 | Beta | x86_64 | >= 3.4 |
| SLES 15 | Beta | x86_64 / aarch64 | >= 3.3 |
| SLES 16 | Alpha | x86_64 / aarch64 | >= 3.3 |
| Ubuntu | Stable | x86_64 / aarch64 | >= 3.0 |
| VMWare ESXi 6 | EOL | x86_64 | >= 3.0 |
| VMWare ESXi 7 | Stable | x86_64 | >= 3.0 |
| VMWare ESXi 8 | Beta | x86_64 | >= 3.0 |
| VMWare ESXi 9 | Beta | x86_64 | >= 3.0 |
| Windows 2016 | Beta | x86_64 | >= 3.3 |
| Windows 2019 | Beta | x86_64 | >= 3.3 |
| Windows 2022 | Beta | x86_64 | >= 3.3 |
| Windows 2025 | Beta | x86_64 | >= 3.3 |
| Windows 10 | Beta | x86_64 | >= 3.3 |
| Windows 11 | Beta | x86_64 | >= 3.3 |
| XenServer 8 | Beta | x86_64 | >= 3.3 |
| XCP-ng 8.x | Beta | x86_64 | >= 3.3 |
- Stable: Tested and suitable for production.
- Beta: Works in most cases but needs broader validation.
- Alpha: Depends on unreleased MAAS or Curtin versions; not production-ready.
- EOL: Upstream OS is no longer supported — not recommended.
- Use
PACKER_LOG=1to enable verbose logging. - Use
FOREGROUND=1to keep processes in the foreground. - To view the build VM:
- Remove
headless=truein the template, or - Connect via VNC using the IP/port printed during build.
- Remove
- Follow examples in each OS’s
README.md. - The
nameparameter is formatted asprefix/os-name. Theos-namecan include dashes, dots and numbers but no space and special characters. - Use unique names for images to avoid cache collisions.
- The
titleparameter is free text format as long as enclosed in quotes. - Upload from a machine close to the MAAS region controller to reduce latency.
- Test images on a small scale before wide deployment.
We welcome contributions.
Each OS directory typically contains:
- One or more
.pkr.hcltemplates scripts/for provisioninghttp/for installer automation- A
README.mdwith OS-specific instructions - A
Makefilefor build automation
- Fork the repo.
- Create a branch.
- Add a new directory or
.pkr.hcltemplate. - Run
packer validate .to check. - Commit and push.
- Open a pull request.