Skip to main content

Overview

Modifying a cloud image offline — before uploading — allows you to install packages, configure software, inject files, and set system parameters without needing to launch an instance. This approach produces consistent, repeatable golden images for deployment. All modifications are performed on a copy of the image using libguestfs tools.
Prerequisites
  • A Linux workstation or build server with libguestfs-tools installed
  • A base cloud image in QCOW2 or RAW format (see Get Cloud Images)
  • Root or sudo access on the build host
  • Sufficient disk space: at least 3x the image size for safe modification
Always work on a copy of the image, not the original. If a modification fails or produces an unusable image, the original remains intact.

Tools Reference

ToolPurposeBest For
virt-customizeApply changes to an image non-interactivelyInstalling packages, running scripts, injecting files
guestfishInteractive filesystem shell for an imageManual file edits, inspecting content, complex changes
guestmountMount an image as a local directoryInteractive editing with standard filesystem tools
virt-sysprepReset and generalize an image for distributionRemoving machine-specific data before capturing

Modify with virt-customize

virt-customize applies changes to an image from the command line without mounting it. It is the fastest method for automatable, scripted modifications.

Install libguestfs-tools

Install on Ubuntu / Debian
apt-get update && apt-get install -y libguestfs-tools
Install on CentOS / AlmaLinux / Rocky Linux
dnf install -y libguestfs-tools

Copy the base image

Create a working copy of the image
cp ubuntu-24.04-lts.qcow2 ubuntu-24.04-lts-custom.qcow2

Install packages

Install software into the image using the native package manager:
Install packages with virt-customize
virt-customize \
  -a ubuntu-24.04-lts-custom.qcow2 \
  --install nginx,curl,htop \
  --update
The --update flag runs a full package upgrade before installing the specified packages.

Inject an SSH key

Pre-authorize an SSH public key for the default user:
Inject SSH public key
virt-customize \
  -a ubuntu-24.04-lts-custom.qcow2 \
  --ssh-inject ubuntu:file:/home/user/.ssh/id_ed25519.pub

Run a configuration script

Execute a shell script inside the image to perform complex configuration:
Run a script inside the image
virt-customize \
  -a ubuntu-24.04-lts-custom.qcow2 \
  --run-command 'systemctl enable nginx' \
  --run-command 'echo "CUSTOM_IMAGE=true" >> /etc/environment'

Set the root password

Setting a root password is only appropriate for testing or emergency access images. Production images should rely on SSH key injection via cloud-init.
Set a root password
virt-customize \
  -a ubuntu-24.04-lts-custom.qcow2 \
  --root-password password:YourSecurePassword123

Modify with guestfish

guestfish provides an interactive shell for directly editing files inside an image. Use it for one-off edits, configuration file changes, or inspecting image contents.

Open the image in guestfish

Open image in interactive mode
guestfish --rw -a ubuntu-24.04-lts-custom.qcow2
At the ><fs> prompt, run:
Mount the root filesystem
><fs> run
><fs> list-filesystems
><fs> mount /dev/sda1 /

Edit a configuration file

Use the edit command to open a file in your default editor:
Edit a file inside the image
><fs> edit /etc/cloud/cloud.cfg
Or write content directly with write:
Write content to a file
><fs> write /etc/motd "Xloud Custom Image - $(date +%Y-%m)\n"

Copy files into the image

Upload a local file into the image:
Copy local file into the image
><fs> upload /local/path/custom-config.conf /etc/app/custom-config.conf

Exit and close

Exit guestfish
><fs> umount /
><fs> exit
Changes are written to the image file when guestfish exits.

Mount with guestmount

guestmount mounts the image filesystem as a local directory, enabling standard filesystem tools (cp, vim, chmod) to operate on image contents.

Create a mount point

Create a temporary mount directory
mkdir -p /mnt/image

Mount the image

Mount the image read-write
guestmount -a ubuntu-24.04-lts-custom.qcow2 -i --rw /mnt/image
The -i flag auto-detects the root filesystem.

Make modifications

The image contents are accessible as a regular directory:
Edit a file in the mounted image
echo "net.ipv4.tcp_tw_reuse = 1" >> /mnt/image/etc/sysctl.d/99-custom.conf
cp /local/app/configs/* /mnt/image/etc/app/
chmod 640 /mnt/image/etc/app/*.conf

Unmount when complete

Unmount the image
guestunmount /mnt/image
All changes are written to the image file. Verify with guestfish if needed.

Generalize with virt-sysprep

Before distributing or uploading an image as a golden template, use virt-sysprep to remove machine-specific identifiers. This ensures each launched instance is treated as a fresh system rather than a clone of the build host.
Generalize the image for distribution
virt-sysprep \
  -a ubuntu-24.04-lts-custom.qcow2 \
  --operations defaults,-ssh-hostkeys \
  --selinux-relabel
virt-sysprep removes:
  • Machine ID (/etc/machine-id)
  • Shell history files
  • Temporary files and package caches
  • Log files
  • NetworkManager connection caches
Use --operations defaults,-ssh-hostkeys to keep cloud-init’s SSH key regeneration intact. The -ssh-hostkeys exclusion prevents virt-sysprep from removing keys that cloud-init would regenerate anyway.

Next Steps

Image Requirements

Verify your modified image meets all Xloud Compute compatibility requirements.

Convert Formats

Convert VMDK, VHD, or RAW images to QCOW2 after modification.

Upload an Image

Upload the customized image to the Xloud Image Service.

Image Properties

Tag uploaded images with OS metadata and hardware requirements.