Manage Container and VM Images with systemd-importd

systemd-importd is the background service behind every machinectl import, export and download of a container or VM image. You will finish with a safe workflow for inspecting image storage and moving images in and out of it without guessing at what changed. The examples match systemd 255.4-1ubuntu8.17 installed on Ubuntu here.

Allow about 20 minutes for a read-only check, or longer for an actual image transfer. You need a shell, the systemd package and an image source you trust. Importing, downloading, exporting and removing images changes system state and normally needs elevated privileges. This guide does not start, stop or remove a machine.

1. Confirm the installed interface

Check the version and the command path first. These are ordinary, read-only commands:

$ machinectl --version
systemd 255 (255.4-1ubuntu8.17)
$ command -v machinectl
/usr/bin/machinectl
$ command -v systemd-importd || true
$ ls -l /usr/lib/systemd/systemd-importd
-rwxr-xr-x 1 root root ... /usr/lib/systemd/systemd-importd

The daemon is the service implementation, not the command you actually type. machinectl asks systemd's D-Bus service to perform the transfer, so do not try to invoke /usr/lib/systemd/systemd-importd directly as a replacement for it.

Checkpoint: Confirm the operations available on this host:

$ machinectl --help | sed -n '/Image Transfer Commands:/,/Options:/p'
Image Transfer Commands:
  pull-tar URL [NAME]         Download a TAR container image
  pull-raw URL [NAME]         Download a RAW container or VM image
  import-tar FILE [NAME]      Import a local TAR container image
  import-raw FILE [NAME]      Import a local RAW container or VM image
  import-fs DIRECTORY [NAME]  Import a local directory container image
  export-tar NAME [FILE]      Export a TAR container image locally
  export-raw NAME [FILE]      Export a RAW container or VM image locally

2. Inspect images and transfers without changing anything

List images before choosing a destination name. This normally does not need sudo:

$ machinectl --no-pager list-images
MACHINE CLASS SERVICE OS VERSION ADDRESSES
        ...

The exact columns and rows depend on the host. An empty table means no image is currently visible to machinectl, not proof that an import failed. Check transfers separately:

$ machinectl --no-pager list-transfers
IDX TYPE     SOURCE DESTINATION     PROGRESS
...  ...      ...    ...              ...

No rows is normal when nothing is downloading or importing. If a transfer is active, record its index before considering cancellation, because cancellation itself changes state:

$ sudo machinectl cancel-transfer TRANSFER_ID

Replace TRANSFER_ID with an identifier from the list, not the image name. If you cancel a transfer, rerun list-transfers and keep the partial result out of service until you have checked what it left behind.

3. Choose a name and verify the source

Image names live below /var/lib/machines. Choose a short, unique name such as test-debian, and check the destination before importing:

$ IMAGE_NAME=test-debian
$ machinectl image-status "$IMAGE_NAME"
Failed to query image: No such file or directory

That missing-image error is useful here: it shows the name is not already in use. If you see image properties instead, stop and choose another name or deliberately plan a replacement.

For a local archive, inspect it without extracting it:

$ tar -tf /path/to/container.tar | sed -n '1,20p'
$ test -r /path/to/container.tar && echo 'archive is readable'
archive is readable

An archive that opens cleanly is not the same as an archive worth trusting. Confirm its provenance, expected operating system and intended architecture. For a remote URL, use a trusted HTTPS source and follow that publisher's checksum or signature instructions. The import service can verify some downloads when the relevant machinectl verification options and signed metadata are available, but a transfer completing is not a security review.

4. Import a local TAR image

Warning: This writes an image into /var/lib/machines. Check the name and archive first, and keep the original archive until the result has been inspected.

Import a TAR container image with root privileges:

$ sudo machinectl import-tar /path/to/container.tar "$IMAGE_NAME"
Enqueued transfer job for container image.

The wording can vary by systemd version, so verify the result rather than trusting the message:

$ machinectl --no-pager image-status "$IMAGE_NAME"
NAME        TYPE RO USAGE CREATED MODIFIED PATH
test-debian  dir  no ...    ...     ...      /var/lib/machines/test-debian

A TAR import is stored as a directory tree or Btrfs subvolume under the chosen name. The service may start on demand, so an inactive result from systemctl is-active systemd-importd while no transfer is running is not by itself an error.

Recovery: If the imported image is wrong, do not overwrite it by guessing at a force option. Preserve any needed logs or data first, then remove the named image only after checking the target:

$ machinectl image-status "$IMAGE_NAME"
$ sudo machinectl remove "$IMAGE_NAME"
$ machinectl image-status "$IMAGE_NAME"
Failed to query image: No such file or directory

remove is destructive. Treat the second command as a deliberate deletion, not routine cleanup.

5. Import a directory for a quick local test

If you already have a prepared root directory, import-fs imports it as a container image. This also changes /var/lib/machines, so use a disposable directory and a distinct name:

$ sudo machinectl import-fs /path/to/prepared-root test-directory
$ machinectl --no-pager image-status test-directory

The source directory is not a generic installer: it must already contain a usable container filesystem, and importing it does not prove the container will boot. Check its files and metadata before attempting to run it. To undo this test, verify the exact name and use sudo machinectl remove test-directory.

6. Export an image without replacing an existing file

Exporting reads an image and writes a new archive. Use a new destination so shell redirection or an existing path cannot destroy a useful backup:

$ OUTPUT=/tmp/test-debian.tar.new
$ sudo machinectl export-tar "$IMAGE_NAME" "$OUTPUT"
$ test -s "$OUTPUT" && tar -tf "$OUTPUT" | sed -n '1,10p'
$ mv -- "$OUTPUT" /path/to/backup/test-debian.tar

The final mv is only safe once the temporary file is non-empty and its contents look right. If the export fails, remove the incomplete file in /tmp and the original backup stays untouched. Use export-raw with a destination ending in .raw when you need a raw VM or container image rather than a TAR archive.

7. Download only when verification and storage are clear

pull-tar and pull-raw download directly through the same service. A download is both network activity and a write into local image storage:

$ sudo machinectl pull-tar https://example.invalid/trusted-image.tar test-remote
$ machinectl --no-pager list-transfers
$ machinectl --no-pager image-status test-remote

That URL is a placeholder and is not expected to work. Replace it only with a source you have independently verified, and never paste an untrusted URL into a privileged command. Use the command's supported verification settings where the source publishes matching signatures or checksums, and watch the transfer list until it disappears before treating the image as ready.

Done means