Package a VM disk image as a container disk image

Before you deploy a VM application, package your qcow2 or raw disk image as an OCI container disk image and push it to an OCI-compatible registry. The container disk image must place the disk file in the /disk/ directory, owned by UID 107, with permissions set to 0440.

Before you begin

  • A qcow2 or raw VM guest disk image file. If you plan to use SSH keys, passwords, or other cloud-init configuration in the VM application, the guest image must include cloud-init installed and enabled. When you build custom Red Hat Enterprise Linux guest images from bootc, install cloud-init in the Containerfile and enable cloud-init.target before you create the disk image. For a Containerfile example, see Build for specific target platforms in the Additional resources section.
  • Podman is installed on your local machine.
  • Access to an OCI-compatible registry with push permissions.
  • An active registry session established with podman login.

Procedure

  1. Create a Containerfile that packages your disk image.

    Use the following simple Containerfile for a scratch-based container disk image:

    FROM scratch
    ADD --chown=107:107 <disk_image_file> /disk/

    Replace <disk_image_file> with the name of your qcow2 or raw disk image file, such as rhel9-guest.qcow2.

    Note

    The file in /disk/ must be owned by UID 107 with permissions 0440. The --chown=107:107 flag in the ADD instruction sets the correct ownership. The scratch-based approach does not set file permissions because scratch images have no shell to run chmod. The resulting file permissions depend on the source file permissions. If your source file does not already have 0440 permissions, use the multi-stage approach instead.

    Alternatively, use a multi-stage Containerfile with a ubi9 builder stage to set exact permissions:

    FROM registry.access.redhat.com/ubi9/ubi:latest AS builder
    COPY <disk_image_file> /tmp/disk/
    RUN chmod 0440 /tmp/disk/<disk_image_file> && \
        chown 107:107 /tmp/disk/<disk_image_file>
    
    FROM scratch
    COPY --from=builder /tmp/disk/ /disk/
  2. Build the container disk image:
    $ podman build -t <registry>/<repository>:<tag> -f Containerfile .
  3. Push the container disk image to your registry:
    $ podman push <registry>/<repository>:<tag>

Results

  • Verify that the push succeeded by inspecting the image in the remote registry:
    $ skopeo inspect docker://<registry>/<repository>:<tag>

    The output must include the image digest and media type, confirming the image is accessible from the registry.

  • Verify the container disk image layout by creating a temporary container and copying out the /disk/ directory: Note

    Scratch-based images contain no shell or utilities, so podman run commands such as ls do not work. Use podman create and podman cp instead.

    $ podman create --name tmp-verify <registry>/<repository>:<tag>
    $ podman cp tmp-verify:/disk/ ./disk-check/
    $ ls -la ./disk-check/
    $ podman rm tmp-verify

    The output of ls -la ./disk-check/ must show the disk image file owned by UID 107 with permissions 0440:

    -r--r-----. 1 107 107 1073741824 Jul 14 10:00 rhel9-guest.qcow2
Note

For testing, you can skip the packaging step and use a pre-built community container disk image such as quay.io/containerdisks/fedora:40. Pre-built community images are not supported by Red Hat.

Note

In air-gapped environments, mirror the container disk image to a registry accessible from the edge device. The Red Hat Edge Manager agent must be able to pull the image before starting the VM.