Usage Guide#

A practical guide for common ICT workflows. For the complete command reference, see the CLI Specification.

Table of Contents#


Binary Location#

The path to the image-composer-tool binary depends on how you built or installed it:

Build method

Binary path

go build ./cmd/image-composer-tool

./image-composer-tool

earthly +build

./build/image-composer-tool

Debian package

image-composer-tool (installed to /usr/local/bin/)

The examples below use ./image-composer-tool (the go build location). Substitute the path that matches your setup.

Commands Overview#

image-composer-tool build         # Build an image from a template
image-composer-tool validate      # Validate a template without building
image-composer-tool resolve       # Print the merged template YAML for debugging
image-composer-tool inspect       # Inspect a raw image's structure
image-composer-tool compare       # Compare two images
image-composer-tool ai            # AI-powered template generation (RAG)
image-composer-tool serve         # Run the web UI backend API server
image-composer-tool cache clean   # Manage cached artifacts
image-composer-tool config        # Manage configuration (init, show)
image-composer-tool version       # Display version info
image-composer-tool --help        # Show all commands and options

For the full details on every command — including inspect, compare, and cache — see the CLI Specification.

Building an Image#

ISO images require the live-installer binary. Build it before starting an ISO build:

go build -buildmode=pie -o ./build/live-installer ./cmd/live-installer

If you use earthly +build, both binaries are built automatically. See the Installation Guide for details.

# go build — binary is in the repo root
sudo -E ./image-composer-tool build image-templates/azl3/azl3-x86_64-edge-raw.yml

# earthly +build — binary is in ./build/
sudo -E ./build/image-composer-tool build image-templates/azl3/azl3-x86_64-edge-raw.yml

# Debian package — binary is on PATH
sudo image-composer-tool build /usr/share/image-composer-tool/examples/azl3-x86_64-edge-raw.yml

# Override config settings with flags
sudo -E ./image-composer-tool build --workers 16 --cache-dir /tmp/cache image-templates/azl3/azl3-x86_64-edge-raw.yml

# Build from scratch in throwaway cache/workspace dirs (removed after the build)
sudo -E ./image-composer-tool build --no-cache image-templates/azl3/azl3-x86_64-edge-raw.yml

# Override composition settings for one build (disk strategy, hostname, proxy, SSH keys)
sudo -E ./image-composer-tool build --disk-strategy largest --hostname edge-01 \
  --http-proxy http://proxy.example.com:3128 --ssh-authorized-key admin=$HOME/.ssh/id_ed25519.pub \
  image-templates/ubuntu24/ubuntu24-x86_64-edgepack-unattended-iso.yml

Common flags: --workers, --cache-dir, --work-dir, --no-cache, --verbose, --dotfile, --config, --log-level. Composition overrides: --disk-strategy, --hostname, --http-proxy, --https-proxy, --ftp-proxy, --no-proxy, --ssh-authorized-key. See the full build flag reference for descriptions and additional flags like --system-packages-only.

Build Output#

After the image finishes building, the output is placed under the configured work_dir. The full path follows this pattern:

<work_dir>/<os>-<dist>-<arch>/imagebuild/<system-config-name>/

The default work_dir depends on how you installed the tool:

Install method

Default work_dir

Example output path

Cloned repo

./workspace (relative to repo root)

./workspace/azure-linux-azl3-x86_64/imagebuild/edge/

Debian package

/tmp/image-composer-tool

/tmp/image-composer-tool/azure-linux-azl3-x86_64/imagebuild/edge/

You can override it with --work-dir or by setting work_dir in your configuration file.

An ISO build also leaves the SPDX SBOM of the installed system (spdx_manifest_*.json), a composition manifest (<image-name>-<version>.composition.json), and the merged template (template-dump.yaml) in this directory. See the Unattended ISO Installer Tutorial.

Comparing Overlay Outputs#

An overlay build layers packages onto an existing baseline RAW image and emits, alongside the result, SPDX SBOM sidecars (see SBOM generation (overlay mode)):

Artifact

Name

Overlay RAW image

<name>-<version>.raw

Delta SBOM (overlay changes only)

<name>-<version>.delta.spdx.json

Complete SBOM (full final inventory)

<name>-<version>.complete.spdx.json (only when a base SBOM is available; otherwise skipped)

The existing compare command works directly against these outputs — no overlay-specific flags are needed. The examples below assume the baseline is at baseline.raw and the overlay build produced myimage-1.0.* under the build output directory.

Baseline RAW vs Overlay RAW#

Compare the structural/binary differences between the baseline and the overlay result. Add --hash-images to compute SHA256 and get a binary-identity verdict:

# Human-readable structural diff (partition table, filesystems, bootloader, SBOM metadata)
./image-composer-tool compare baseline.raw myimage-1.0.raw

# High-level counts as JSON, suitable for CI
./image-composer-tool compare --format=json --mode=summary baseline.raw myimage-1.0.raw

# Binary-identity check (slower — hashes both images)
./image-composer-tool compare --hash-images baseline.raw myimage-1.0.raw

Expected output pattern (text): a top Equality: verdict followed by the changed sections. Because the overlay grows the disk and rewrites the embedded SBOM, the verdict is different, and the summary reports sbomChanged and a size change:

Equality: different (meaningful diffs: ...)
Image:
  Size: 4.0 GiB -> 6.0 GiB
SBOM:
  PackageCount: 120 -> 123

The equality classes are binary_identical (only with --hash-images, when the SHA256s match), semantically_identical / semantically_identical_unverified (no meaningful diffs), or different.

Note: In the default image modes the compare tool reports SBOM metadata (package count, canonical hash) only. For a package-by-package breakdown, use the SBOM comparison below.

SBOM Comparison (Complete SBOM)#

Use --mode=spdx for an accurate package-level diff. The complete SBOM represents the full final image inventory, so comparing the baseline’s SBOM against the overlay complete SBOM reports exactly which packages were added or upgraded:

# Compare the baseline SBOM against the overlay complete SBOM
./image-composer-tool compare --mode=spdx baseline.spdx.json myimage-1.0.complete.spdx.json

# Either argument may also be an image — its embedded SBOM (/usr/share/sbom) is
# extracted automatically, so you can compare the baseline RAW directly:
./image-composer-tool compare --mode=spdx baseline.raw myimage-1.0.complete.spdx.json

# JSON for tooling
./image-composer-tool compare --format=json --mode=spdx baseline.spdx.json myimage-1.0.complete.spdx.json

Expected output pattern (text): added, removed, and upgraded package lists. A same-name version bump is reported as a single upgrade (~), not a remove + add:

SPDX Compare
============
Equal:    false
Packages: 120 -> 123

Added packages:
  + tree|2.1.1|...

Upgraded packages:
  ~ curl: 8.5.0 -> 8.6.0

Overlay builds are additive by default, so you will typically see additions and upgrades. Removals appear only when overlayPolicy.allowPackageRemoval is enabled (a conflict-driven baseline package removal).

Delta SBOM: Optional Quick-Change View#

The delta SBOM lists only the overlay-contributed packages (what changed). It is an optional convenience for a quick “what did this overlay add?” view without diffing against the baseline — you can inspect it directly or diff it against an empty document:

# The delta SBOM is a plain SPDX JSON file — read it directly
jq '.packages[].name' myimage-1.0.delta.spdx.json

# Or diff it against an empty SBOM to render the same added-package list via compare
echo '{"packages":[]}' > empty.spdx.json
./image-composer-tool compare --mode=spdx empty.spdx.json myimage-1.0.delta.spdx.json

For an authoritative before/after picture of the image, prefer the complete SBOM comparison above; the delta is a shortcut, not a replacement.

Validating a Template#

Check a template for errors before starting a build:

./image-composer-tool validate image-templates/azl3/azl3-x86_64-edge-raw.yml

Resolving a Template#

Print the merged template YAML to stdout — useful for debugging templates that use extends: or for previewing exactly what the tool will build. Sensitive fields (user passwords, systemConfig.users[*].hash_algo, the secure boot signing paths systemConfig.immutability.secureBootDBKey, systemConfig.immutability.secureBootDBCrt, and systemConfig.immutability.secureBootDBCer, and the DKMS secure boot signing paths systemConfig.dkms.secureBoot.signingKeyPath, systemConfig.dkms.secureBoot.signingCertPath, systemConfig.dkms.secureBoot.targetKeyPath, and systemConfig.dkms.secureBoot.targetCertPath) are always redacted in the output:

# Chain-merge only, without OS defaults
./image-composer-tool resolve image-templates/ubuntu24/ubuntu24-x86_64-extends-example-raw.yml

# Full build-time view: extends chain + OS defaults
./image-composer-tool resolve image-templates/azl3/azl3-x86_64-edge-raw.yml --full

Debugging an Extends Chain#

When a template uses extends: to inherit from a parent, resolve folds the chain and prints the effective template so you can verify what the leaf actually inherits before running a build:

# Show what an extends child inherits from its parent
./image-composer-tool resolve image-templates/ubuntu24/ubuntu24-x86_64-extends-example-raw.yml

The output includes the union of the parent’s and child’s systemConfig.packages, plus every other field folded through the chain’s per-section merge rules. OS defaults are not applied unless you pass --full, so the output reflects only what the templates in the chain declared — useful for spotting an accidentally-overridden field. For the complete inheritance semantics (single-parent chains, cycle detection, target-match rules, and the depth-warning threshold), see Template Extends (Inheritance).

Configuration#

The tool uses a layered configuration: config file values are overridden by command-line flags. A config file is auto-discovered from several standard locations (current directory, home directory, /etc/), or you can specify one explicitly with --config.

# Create a default configuration file
./image-composer-tool config init

# Show the active configuration
./image-composer-tool config show

# Use a specific configuration file
./image-composer-tool --config /path/to/config.yaml build template.yml

Key settings:

Setting

Default (cloned repo)

Default (Debian pkg)

workers

8

8

cache_dir

./cache

/var/cache/image-composer-tool

work_dir

./workspace

/tmp/image-composer-tool

For the complete search order and all configuration fields, see Configuration Files in the CLI Specification.

Operations Requiring Sudo#

The build command requires sudo because it performs system-level operations: creating loop devices, mounting filesystems, setting up chroot environments, installing packages, and configuring bootloaders.

Always run builds with sudo -E to preserve your environment variables (such as $PATH and proxy settings).

Running the Web UI#

serve starts the HTTP API behind the web UI and serves the frontend bundle embedded in the binary. Run it from the repository root — it resolves --templates-dir, --work-dir, and the configuration file relative to its working directory:

# Start on http://127.0.0.1:8080
./image-composer-tool serve

# Pick a different port
./image-composer-tool serve --port 9090

Builds still need root. Run the server as root on an isolated build host, or run it as an unprivileged user with --sudo plus the scoped sudoers rules that let it cancel builds and read root-owned artifacts. Generate and install those rules rather than writing them by hand:

# Generate, visudo-validate, and install in one step
scripts/install-sudoers.sh

# Then run builds under sudo -n
./image-composer-tool serve --sudo

The server binds to localhost by default and has no authentication of its own. Because the API can start privileged builds, exposing it with --host 0.0.0.0 should be a deliberate decision, not a convenience.

The OS manifest, the repository catalog, and the Edge Pack catalog are embedded at build time. To edit any of them and see the change after a restart instead of a rebuild, point the matching flag at a file on disk:

./image-composer-tool serve \
  --package-repos ./internal/api/service/data/package-repos.yaml \
  --edge-pack ./internal/api/service/data/edge-pack.yaml

A malformed or missing override file stops the server at startup rather than falling back to the embedded copy.

For every flag, the sudoers rules in full, and their security implications, see the Serve Command reference. For building the frontend bundle and the hot-reload development loop, see web/README.md.

Shell Completion#

# Auto-detect shell and install completion
./image-composer-tool install-completion

# Or specify a shell: bash, zsh, fish, powershell
./image-composer-tool install-completion --shell bash

After installing, reload your shell configuration (e.g., source ~/.bashrc). For per-shell activation steps and manual completion script generation, see the Install-Completion Command reference.

Template Examples#

Templates are YAML files that define the requirements for an image build. For the full template system documentation, see Creating and Reusing Image Templates.

The image-templates/ directory contains ready-to-use examples for all supported distributions and image types.

Minimal Edge Device#

image:
  name: minimal-edge
  version: "1.0.0"

target:
  os: azure-linux
  dist: azl3
  arch: x86_64
  imageType: raw

systemConfig:
  name: minimal
  description: Minimal edge device configuration
  packages:
    - openssh-server
    - ca-certificates
  kernel:
    version: "6.12"
    cmdline: "quiet"

Development Environment#

image:
  name: dev-environment
  version: "1.0.0"

target:
  os: azure-linux
  dist: azl3
  arch: x86_64
  imageType: raw

systemConfig:
  name: development
  description: Development environment with tools
  packages:
    - openssh-server
    - git
    - docker-ce
    - vim
    - curl
    - wget
    - python3
  kernel:
    version: "6.12"
    cmdline: "quiet splash"

Edge Microvisor Toolkit#

image:
  name: emt-edge-device
  version: "1.0.0"

target:
  os: edge-microvisor-toolkit
  dist: emt3
  arch: x86_64
  imageType: raw

systemConfig:
  name: edge
  description: Edge Microvisor Toolkit configuration
  packages:
    - cloud-init
    - rsyslog
  kernel:
    version: "6.12"
    cmdline: "console=ttyS0,115200 console=tty0 loglevel=7"