Chapter 4. Creating system images using the image builder command-line interface
Image builder is a tool for creating custom system images. To control image builder and create your custom system images, you can use the command-line interface (CLI) or the web console interface. Currently, however, the CLI is the preferred method to use image builder.
The image builder tool does not support building RHEL 9 images on a RHEL 8 system. You can only build an image from the earlier version of the image you use in the host distribution.
4.1. Introducing the image builder command-line interface
The image builder command-line interface (CLI) is currently the preferred method to use image builder. It offers more functionality than the web console interface. To use the CLI, run the composer-cli
command with the suitable options and subcommands.
The workflow for the command-line interface can be summarized as follows:
- Export (save) the blueprint definition to a plain text file
- Edit this file in a text editor
- Import (push) the blueprint text file back into image builder
- Run a compose to build an image from the blueprint
- Export the image file to download it
Apart from the basic subcommands to achieve this procedure, the composer-cli
command offers many subcommands to examine the state of configured blueprints and composes.
To run the composer-cli
commands as non-root, the user must be in the weldr
or root
groups.
To add a user to the
weldr
orroot
groups, run the following commands:$ sudo usermod -a -G weldr user $ newgrp weldr
4.2. Creating an image builder blueprint using the command-line interface
You can create a new image builder blueprint using the command-line interface (CLI). The blueprint describes the final image and its customizations, such as packages, and kernel customizations.
Prerequisite
- Access to the image builder tool.
Procedure
Create a plain text file with the following contents:
name = "BLUEPRINT-NAME" description = "LONG FORM DESCRIPTION TEXT" version = "0.0.1" modules = [] groups = []
Replace BLUEPRINT-NAME and LONG FORM DESCRIPTION TEXT with a name and description for your blueprint.
Replace 0.0.1 with a version number according to the Semantic Versioning scheme.
For every package that you want to be included in the blueprint, add the following lines to the file:
[[packages]] name = "package-name" version = "package-version"
Replace package-name with the name of the package, such as httpd, gdb-doc, or coreutils.
Replace package-version with the version to use. This field supports
dnf
version specifications:- For a specific version, use the exact version number such as 8.7.0.
- For the latest available version, use the asterisk *.
- For the latest minor version, use formats such as 8.*.
Customize your blueprints to suit your needs. For example, disable Simultaneous Multi Threading (SMT), add the following lines to the blueprint file:
[customizations.kernel] append = "nosmt=force"
For additional customizations available, see Supported Image Customizations.
- Save the file, for example, as BLUEPRINT-NAME.toml and close the text editor.
Push (import) the blueprint:
# composer-cli blueprints push BLUEPRINT-NAME.toml
Replace BLUEPRINT-NAME with the value you used in previous steps.
NoteTo create images using
composer-cli
as non-root, add your user to theweldr
orroot
groups.# usermod -a -G weldr user $ newgrp weldr
Verification
List the existing blueprints to verify that the blueprint has been pushed and exists:
# composer-cli blueprints list
Display the blueprint configuration you have just added:
# composer-cli blueprints show BLUEPRINT-NAME
Check whether the components and versions listed in the blueprint and their dependencies are valid:
# composer-cli blueprints depsolve BLUEPRINT-NAME
If image builder is unable to depsolve a package from your custom repositories, follow the steps:
Remove the osbuild-composer cache:
$ sudo rm -rf /var/cache/osbuild-composer/* $ sudo systemctl restart osbuild-composer
4.3. Editing an image builder blueprint with command-line interface
You can edit an existing image builder blueprint in the command-line (CLI) interface to, for example, add a new package, or define a new group, and to create your customized images. For that, follow the steps:
Prerequisites
- You have created a blueprint.
Procedure
Save (export) the blueprint to a local text file:
# composer-cli blueprints save BLUEPRINT-NAME
- Edit the BLUEPRINT-NAME.toml file with a text editor and make your changes.
Before finishing the edits, verify that the file is a valid blueprint:
Remove this line, if present:
packages = []
- Increase the version number, for example, fro 0.0.1 to 0.1.0. Remember that image builder blueprint versions must use the Semantic Versioning scheme. Note also that if you do not change the version, the patch version component increases automatically.
Check if the contents are valid TOML specifications. See the TOML documentation for more information.
NoteTOML documentation is a community product and is not supported by Red Hat. You can report any issues with the tool at https://github.com/toml-lang/toml/issues
- Save the file and close the text editor.
Push (import) the blueprint back into image builder:
# composer-cli blueprints push BLUEPRINT-NAME.toml
NoteTo import the blueprint back into image builder, supply the file name including the
.toml
extension, while in other commands use only the blueprint name.To verify that the contents uploaded to image builder match your edits, list the contents of blueprint:
# composer-cli blueprints show BLUEPRINT-NAME
Check whether the components and versions listed in the blueprint and their dependencies are valid:
# composer-cli blueprints depsolve BLUEPRINT-NAME
Additional resources
4.4. Creating a system image with image builder in the command-line interface
You can build a custom image using the image builder command-line interface.
Prerequisites
- You have a blueprint prepared for the image. See Creating an image builder blueprint using the command-line interface.
Procedure
Start the compose:
# composer-cli compose start BLUEPRINT-NAME IMAGE-TYPE
Replace BLUEPRINT-NAME with name of the blueprint, and IMAGE-TYPE with the type of the image. For the available values, see the output of the
composer-cli compose types
command.The compose process starts in the background and shows the composer Universally Unique Identifier (UUID).
Wait until the compose process is finished. The image creation can take up to ten minutes to complete.
To check the status of the compose:
# composer-cli compose status
A finished compose shows the FINISHED status value. To identify your compose in the list, use its UUID.
After the compose process is finished, download the resulting image file:
# composer-cli compose image UUID
Replace UUID with the UUID value shown in the previous steps.
Verification
After you create your image, you can check the image creation progress using the following commands:
Check the compose status:
$ sudo composer-cli compose status
Download the metadata of the image:
$ sudo composer-cli compose metadata UUID
Download the logs of the image:
$ sudo composer-cli compose logs UUID
The command creates a
.tar
file that contains the logs for the image creation. If the logs are empty, you can check the journal.Check the journal:
$ journalctl | grep osbuild
Check the manifest:
$ sudo cat /var/lib/osbuild-composer/jobs/job_UUID.json
You can find the job_UUID.json in the journal.
Additional resources
4.5. Basic image builder command-line commands
The image builder command-line interface offers the following subcommands.
Blueprint manipulation
- List all available blueprints
# composer-cli blueprints list
- Show a blueprint contents in the TOML format
# composer-cli blueprints show BLUEPRINT-NAME
- Save (export) blueprint contents in the TOML format into a file
BLUEPRINT-NAME.toml
# composer-cli blueprints save BLUEPRINT-NAME
- Remove a blueprint
# composer-cli blueprints delete BLUEPRINT-NAME
- Push (import) a blueprint file in the TOML format into image builder
# composer-cli blueprints push BLUEPRINT-NAME
Composing images from blueprints
- List the available image types
# composer-cli compose types
- Start a compose
# composer-cli compose start BLUEPRINT COMPOSE-TYPE
Replace BLUEPRINT with the name of the blueprint to build, and COMPOSE-TYPE with the output image type.
- List all composes
# composer-cli compose list
- List all composes and their status
# composer-cli compose status
- Cancel a running compose
# composer-cli compose cancel COMPOSE-UUID
- Delete a finished compose
# composer-cli compose delete COMPOSE-UUID
- Show detailed information about a compose
# composer-cli compose info COMPOSE-UUID
- Download image file of a compose
# composer-cli compose image COMPOSE-UUID
- See more subcommands and options
# composer-cli help
Additional resources
- composer-cli(1) man page
4.6. Image builder blueprint format
Image builder blueprints are presented to the user as plain text in the TOML format.
The elements of a typical blueprint file include the following:
- The blueprint metadata
name = "BLUEPRINT-NAME" description = "LONG FORM DESCRIPTION TEXT" version = "VERSION"
The BLUEPRINT-NAME and LONG FORM DESCRIPTION TEXT field are a name and description for your blueprint.
The VERSION is a version number according to the Semantic Versioning scheme.
This part is present only once for the entire blueprint file.
The modules entry lists the package names and versions of packages to be installed into the image.
The group entry describes a group of packages to be installed into the image. Groups use the following package categories:
- Mandatory
- Default
Optional
Blueprints install the mandatory and default packages. There is no mechanism for selecting optional packages.
- Groups to include in the image
[[groups]] name = "group-name"
The group-name is the name of the group, for example, anaconda-tools, widget, wheel or users.
- Packages to include in the image
[[packages]] name = "package-name" version = "package-version"
package-name is the name of the package, such as httpd, gdb-doc, or coreutils.
package-version is a version to use. This field supports
dnf
version specifications:- For a specific version, use the exact version number such as 8.7.0.
- For latest available version, use the asterisk *.
- For a latest minor version, use a format such as 8.*.
Repeat this block for every package to include.
Currently there are no differences between packages and modules in the image builder tool. Both are treated as RPM package dependencies.
4.7. Supported image customizations
You can customize your image by adding to your blueprint an additional RPM package, by enabling a service, or by customizing a kernel command line parameter. You can use several image customizations within blueprints. To make use of these options, you must configure the customizations in the blueprint and import (push) it to image builder.
These customizations are not supported when using image builder in the web console.
- Select a distribution
name = "blueprint_name" description = "blueprint_version" version = "0.1" distro = "different_minor_version"
Replace "different_minor_version" to build a different minor version, for example, if you want to build a RHEL 9.0, use
distro
= "rhel-90". On a RHEL 9.1, you can build minor versions such as RHEL 9.1, RHEL 8.7 and earlier releases. If you do not specify a distribution, the blueprint uses the host distribution. In case you upgrade the host operating system, the blueprints with no distribution set will build images using the new OS version.- Select a package group
[[packages]] name = "package_group_name"
Replace "package_group_name" with the name of the group. For example, "@server with gui".
- Set the image hostname
[customizations] hostname = "baseimage"
- User specifications for the resulting system image
[[customizations.user]] name = "USER-NAME" description = "USER-DESCRIPTION" password = "PASSWORD-HASH" key = "PUBLIC-SSH-KEY" home = "/home/USER-NAME/" shell = "/usr/bin/bash" groups = ["users", "wheel"] uid = NUMBER gid = NUMBER
The GID is optional and must already exist in the image. Optionally, a package creates it, or the blueprint creates the GID by using the
[[customizations.group]]
entry.ImportantTo generate the
password hash
, you must install python3 on your system.# dnf install python3
Replace PASSWORD-HASH with the actual
password hash
. To generate thepassword hash
, use a command such as:$ python3 -c 'import crypt,getpass;pw=getpass.getpass();print(crypt.crypt(pw) if (pw==getpass.getpass("Confirm: ")) else exit())'
Replace PUBLIC-SSH-KEY with the actual public key.
Replace the other placeholders with suitable values.
You must enter the
name
. You can omit any of the lines that you do not need.Repeat this block for every user to include.
- Group specifications for the resulting system image
[[customizations.group]] name = "GROUP-NAME" gid = NUMBER
Repeat this block for every group to include.
- Set an existing users SSH key
[[customizations.sshkey]] user = "root" key = "PUBLIC-SSH-KEY"
NoteThe "Set an existing user SSH key" customization is only applicable for existing users. To create a user and set an SSH key, see the User specifications for the resulting system image customization.
- Append a kernel boot parameter option to the defaults
[customizations.kernel] append = "KERNEL-OPTION"
- By default, image builder builds a default kernel into the image. But, you can customize the kernel with the following configuration in blueprint
[customizations.kernel] name = "KERNEL-rt"
- Define a kernel name to use in an image
[customizations.kernel.name] name = "KERNEL-NAME"
- Set the timezone and the Network Time Protocol (NTP) servers for the resulting system image
[customizations.timezone] timezone = "TIMEZONE" ntpservers = "NTP_SERVER"
If you do not set a timezone, the system uses Universal Time, Coordinated (UTC) as default. Setting NTP servers is optional.
- Set the locale settings for the resulting system image
[customizations.locale] languages = ["LANGUAGE"] keyboard = "KEYBOARD"
Setting both the language and the keyboard options is mandatory. You can add many other languages. The first language you add will be the primary language and the other languages will be secondary. For example:
[customizations.locale] languages = ["en_US.UTF-8"] keyboard = "us"
To list the values supported by the languages, run the following command:
$ localectl list-locales
To list the values supported by the keyboard, run the following command:
$ localectl list-keymaps
- Firewall customization
Set the firewall for the resulting system image. By default, the firewall blocks all access, except for services that enable their ports explicitly, such as
sshd
. The following blueprint can be used to open other ports or services.If you do not want to use the
[customizations.firewall]
or the[customizations.firewall.services]
, either remove the attributes, or set them to an empty list []. If you only want to use the default firewall setup, you can omit the customization from the blueprint.NoteThe Google and OpenStack templates explicitly disable the firewall for their environment. This cannot be overridden by the blueprint.
[customizations.firewall] ports = ["PORTS"]
Where
ports
is an optional list of strings that contain ports or a range of ports and protocols to open. You can configure ports by using the following format:port:protocol
formatYou can configure the port ranges by using the
portA-portB:protocol
format. For example:[customizations.firewall] ports = ["22:tcp", "80:tcp", "imap:tcp", "53:tcp", "53:udp", "30000-32767:tcp", "30000-32767:udp"]
You can use numeric ports, or their names from the /etc/services
to enable or disable port lists.
- Customize the firewall services
services
is an optional object with the following attributes containing services to enable or disable forfirewalld
:-
enabled
- An optional list of strings for services to enable. disabled
- An optional list of strings for services to disable.Check the available firewall services.
$ firewall-cmd --get-services
In the blueprint, under section
customizations.firewall.service
, specify the firewall services that you want to customize.[customizations.firewall.services] enabled = ["SERVICES"] disabled = ["SERVICES"]
For example:
[customizations.firewall.services] enabled = ["ftp", "ntp", "dhcp"] disabled = ["telnet"]
The services listed in
firewall.services
are different from the service-names available in the/etc/services
file.NoteIf you do not want to customize the firewall services, omit the
[customizations.firewall]
and[customizations.firewall.services]
sections from the blueprint.
-
- Set which services to enable during the boot time
[customizations.services] enabled = ["SERVICES"] disabled = ["SERVICES"]
You can control which services to enable during the boot time. Some image types already have services enabled or disabled to ensure that the image works correctly and this setup cannot be overridden. The
[customizations.services]
customization in the blueprint do not replace these services, but add them to the list of services already present in the image templates.NoteEach time a build starts, it clones the repository of the host system. If you refer to a repository with a large amount of history, it might take some time to clone and it uses a significant amount of disk space. Also, the clone is temporary and the build removes it after it creates the RPM package.
- Specify a custom filesystem configuration
You can specify a custom filesystem configuration in your blueprints and therefore create images with a specific disk layout, instead of the default layout configuration. By using the non-default layout configuration in your blueprints, you can benefit from:
- security benchmark compliance
- protection against out-of-disk errors
- improved performance
consistency with existing setups
To customize the filesystem configuration in your blueprint:
NoteThe filesystem customization is not supported for OSTree systems, because OSTree images have their own mount rule, such as read-only.
[[customizations.filesystem]] mountpoint = "MOUNTPOINT" size = MINIMUM-PARTITION-SIZE
The blueprint supports the following
mountpoints
and their sub-directories:-
/
- the root mount point -
/var
-
/home
-
/opt
-
/srv
-
/usr
-
/app
-
/data
/boot
- Supported from RHEL 8.7 and RHEL 9.1 onward.NoteCustomizing mount points is only supported from RHEL 8.5 and RHEL 9.0 distributions onward, by using the CLI. In earlier distributions, you can only specify the
root
partition as a mount point and specify thesize
argument as an alias for the image size.If you have more than one partition in the customized image, you can create images with a customized file system partition on LVM and resize those partitions at runtime. To do this, you can specify a customized filesystem configuration in your blueprint and therefore create images with the desired disk layout. The default filesystem layout remains unchanged - if you use plain images without file system customization, and
cloud-init
resizes the root partition.NoteFrom 8.6 onward, for the
osbuild-composer-46.1-1.el8
RPM and later version, the physical partitions are no longer available and filesystem customizations create logical volumes.The blueprint automatically converts the file system customization to a LVM partition.
The
MINIMUM-PARTITION-SIZE
value has no default size format. The blueprint customization supports the following values and units: kB to TB and KiB to TiB. For example, you can define the mount point size in bytes:[[customizations.filesystem]] mountpoint = "/var" size = 1073741824
You can also define the mount point size by using units.
NoteYou can only define the mount point size by using units for the package version provided for RHEL 8.6 and RHEL 9.0 distributions onward.
For example:
[[customizations.filesystem]] mountpoint = "/opt" size = "20 GiB" or [[customizations.filesystem]] mountpoint = "/boot" size = "1 GiB"
-
- Create customized directories and files for your image under the
/etc
directory To create customized files and directories in your image, use the
[[customizations.files]]
and the[[customizations.directories]]
blueprint customizations. Currently, you can use these customizations only in the/etc
directory.NoteThese blueprint customizations are supported by all image types, except the image types that deploy OSTree commits, such as
edge-raw-image
,edge-installer
, andedge-simplified-installer
.
Create a custom directory blueprint customization
With the [[customizations.directories]]
blueprint customization, you can create customized directories in the /etc
directory of your image.
If you use the customizations.directories
with a directory path which already exists in the image with mode
, user
or group
already set, the image build fails to prevent changing the ownership or permissions of the existing directory.
With the [[customizations.directories]]
blueprint customization you can:
- Create new directories.
- Set user and group ownership for the directory you are creating.
- Set the directory mode permission in the octal format.
- Ensure that parent directories are created as needed.
To customize a directory configuration in your blueprint, create a file with the following content, for example:
[[customizations.directories]] path = "/etc/directory_name" mode = "octal_access_permission" user = "user_string_or_integer" group = "group_string_or_integer" ensure_parents = boolean
The blueprint entries are described as following:
-
path
- Mandatory - enter the path to the directory that you want to create. It must be an absolute path under the/etc
directory. -
mode
- Optional - set the access permission on the directory, in the octal format. If you do not specify a permission, it defaults to 0755. The leading zero is optional. -
user
- Optional - set a user as the owner of the directory. If you do not specify a user, it defaults toroot
. You can specify the user as a string or as an integer. -
group
- Optional - set a group as the owner of the directory. If you do not specify a group, it defaults toroot
. You can specify the group as a string or as an integer. -
ensure_parents
- Optional - Specify whether you want to create parent directories as needed. If you do not specify a value, it defaults tofalse
.
Create a custom file blueprint customization
You can use the custom file blueprint customization to create new files or to replace existing files. The parent directory of the file you specify must exist, otherwise, the image build fails. Ensure that the parent directory exists by specifying it in the [[customizations.directories]]
customization.
If you combine the files customizations with other blueprint customizations, it might affect the functioning of the other customizations, or it might override the current files customizations. If you are not sure about the customizations, use the appropriate blueprint customization.
With the [[customizations.files]]
blueprint customization you can:
- Create new text files.
- Modifying existing files. WARNING: this can override the existing content.
- Set user and group ownership for the file you are creating.
Set the mode permission in the octal format.
NoteYou cannot create or replace the following files:
-
/etc/fstab
-
/etc/shadow
-
/etc/passwd
-
/etc/group
To customize a file in your blueprint, create a file with the following content, for example:
[[customizations.files]] path = "/etc/directory_name" mode = "octal_access_permission" user = "user_string_or_integer" group = "group_string_or_integer" data = "Hello world!"
The blueprint entries are described as following:
-
path
- Mandatory - enter the path to the file that you want to create. It must be an absolute path under the/etc
directory. -
mode
Optional - set the access permission on the file, in the octal format. If you do not specify a permission, it defaults to 0644. The leading zero is optional. -
user
- Optional - set a user as the owner of the file. If you do not specify a user, it defaults toroot
. You can specify the user as a string or as an integer. -
group
- Optional - set a group as the owner of the file. If you do not specify a group, it defaults toroot
. You can specify the group as a string or as an integer. -
data
- Optional - Specify the content of a plain text file. If you do not specify a content, it creates an empty file.
Additional resources
4.8. Packages installed by image builder
When you create a system image using image builder, the system installs a set of base packages.
Table 4.1. Default packages to support image type creation
Image type | Default Packages |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
When you add additional components to your blueprint, ensure that the packages in the components you added do not conflict with any other package components. Otherwise, the system fails to solve dependencies and creating your customized image fails. You can check if there is no conflict between the packages by running the command:
# composer-cli blueprints depsolve BLUEPRINT-NAME
Additional resources
4.9. Enabled services on custom images
When you use image builder to configure a custom image, the default services that the image uses are determined by the following:
-
The RHEL release on which you use the
osbuild-composer
utility - The image type
For example, the ami
image type enables the sshd
, chronyd
, and cloud-init
services by default. If these services are not enabled, the custom image does not boot.
Table 4.2. Enabled services to support image type creation
Image type | Default enabled Services |
---|---|
| sshd, cloud-init, cloud-init-local, cloud-config, cloud-final |
| sshd, cloud-init, cloud-init-local, cloud-config, cloud-final |
| cloud-init |
| No extra service enables by default |
| No extra service enables by default |
| sshd, chronyd, waagent, cloud-init, cloud-init-local, cloud-config, cloud-final |
| sshd, chronyd, vmtoolsd, cloud-init |
Note: You can customize which services to enable during the system boot. However, the customization does not override services enabled by default for the mentioned image types.
Additional resources