SYSTEMD-DISSECT(1)

πŸ’– SYSTEMD-DISSECT πŸ’–

okay bestie, gather round, because today we're talking about systemd-dissect β€” also known as mount.ddi β€” and it's literally the tool for Dissect Discoverable Disk Images (DDIs). yes the acronym is a whole thing. we love her anyway. πŸŽ€


✨ SYNOPSIS ✨

like, here's every single way you can summon her. no notes, just vibes:

systemd-dissect [OPTIONS...] IMAGE

systemd-dissect [OPTIONS...] [--mount] IMAGE PATH

systemd-dissect [OPTIONS...] [--umount] PATH

systemd-dissect [OPTIONS...] [--attach] IMAGE

systemd-dissect [OPTIONS...] [--detach] PATH

systemd-dissect [OPTIONS...] [--list] IMAGE

systemd-dissect [OPTIONS...] [--mtree] IMAGE

systemd-dissect [OPTIONS...] [--with] IMAGE [COMMAND...]

systemd-dissect [OPTIONS...] [--copy-from] IMAGE PATH [TARGET]

systemd-dissect [OPTIONS...] [--copy-to] IMAGE [SOURCE] PATH

systemd-dissect [OPTIONS...] [--make-archive] IMAGE [TARGET]

systemd-dissect [OPTIONS...] [--discover]

systemd-dissect [OPTIONS...] [--validate] IMAGE

systemd-dissect [OPTIONS...] [--shift] IMAGE UIDBASE

🌸 DESCRIPTION 🌸

so systemd-dissect is your girl for introspecting and interacting with file system OS disk images β€” specifically the Discoverable Disk Images (DDIs) kind. she does four different things, and honestly? each one eats:

  1. Show general OS image information β€” including the image's os-release(5) data, the machine ID, partition info, all of it. it's giving ✨receipts✨.
  2. Mount an OS image to a local directory. in this mode she'll dissect the OS image and mount the included partitions according to their designation onto a directory, and possibly into sub-directories too. very organized of her. πŸ—‚οΈ
  3. Unmount an OS image from a local directory. here she recursively unmounts the mounted partitions and removes the underlying loop device, including all the partition sub-devices. no crumbs left. πŸ’…
  4. Copy files and directories in and out of an OS image. in and out. bidirectional queen. πŸ”„

πŸ¦‹ the three types of OS images she fw πŸ¦‹

the tool may operate on three types of OS images, and yes there's a whole taxonomy:

  1. OS disk images containing a GPT partition table envelope, with partitions marked according to the UAPI.2 Discoverable Partitions Specification[1]. so official. so formatted.
  2. OS disk images containing just a plain file-system without an enveloping partition table. (this file system is assumed to be the root file system of the OS.) simple. minimal. lowkey iconic.
  3. OS disk images containing a GPT or MBR partition table, with a single partition only. (this partition is assumed to contain the root file system of the OS.) one partition, one job, we respect it.

πŸ’œ the deets you actually need πŸ’œ

OS images may use any kind of Linux-supported file systems. and that's not all β€” they may also make use of LUKS disk encryption, and contain Verity integrity information. fancy!! πŸ”βœ¨

ok so this next bit matters: qualifying OS images may be booted with systemd-nspawn(1)'s --image= switch, and can be used as the root file system for a system service using the RootImage= unit file setting β€” peep systemd.exec(5) for that one. 🫢

another thing, bestie, and it's important: the partition table shown when you invoke her without a command switch (as listed below, mwah) does not necessarily show all the partitions in the image β€” just the ones that are understood and considered part of an OS disk image. specifically:

  • partitions of unknown types are ignored,
  • as are duplicate partitions (i.e. more than one per partition type),
  • and so are root and /usr/ partitions of architectures not compatible with the local system.

in other words: this tool displays exactly what she operates with when mounting the image. if you want the complete list of partitions, go use a tool like fdisk(8). she's not doing all that. πŸ’€


🧚 one more thing (the mount.ddi glow-up) 🧚

the systemd-dissect command may be invoked as mount.ddi, in which case she implements the mount(8) "external helper" interface. this means disk images compatible with systemd-dissect can be mounted directly by mount and by fstab(5). yes, directly. no extra steps. for the deets, see below. πŸ‘‘

and also β€” in place of the image path, a ".v/" versioned directory may be specified. see systemd.v(7) for the full rundown on that one. πŸ“βœ¨


πŸŽ€ COMMANDS πŸŽ€

okay so real talk bestie β€” if you don't pass any of the command switches below, the tool just opens the disk image you gave it and shows you the general tea: info about the image, the partitions inside it, and what those partitions are for. that's the default vibe check πŸ’…


--mount, -m ✨

Mounts the OS image you picked into the directory you picked. It dissects the image, figures out the OS root file system β€” and maybe other partitions too β€” and mounts them into that directory. If the OS image has multiple partitions marked with the UAPI.2 Discoverable Partitions Specification[1], you get multiple nested mounts going on. This command wants two arguments: a path to an image file, and a path to a directory where the image should get mounted. 🫢

To unmount an OS image you mounted like this, use the --umount operation.

If the OS image has LUKS encrypted or Verity integrity protected file systems, the right volumes get set up automatically and marked for automatic disassembly when the image is unmounted. no manual cleanup era πŸ’…

The OS image can be either a path to an OS image stored in a regular file, or it can point at a block device node β€” and in that case the block device must be the "whole" device, like, not a partition device. (The other supported commands described here support that too, fyi.)

All mounted file systems get checked with the appropriate fsck(8) implementation in automatic fixing mode β€” unless you explicitly turn it off with --fsck=no, or you requested read-only operation with --read-only.

ngl this functionality is also available in mount(8) via a command like mount -t ddi myimage.raw targetdir/, and in fstab(5) too. Details below, bestie.

Added in version 247.

-M 🌸

Shortcut for --mount --mkdir. that's it that's the whole thing πŸ’–

Added in version 247.

--umount, -u πŸ¦‹

Unmounts an OS image from the directory you specify. Expects one argument: the directory where an OS image was mounted.

All mounted partitions get recursively unmounted, and the underlying loop device gets removed, along with all its partition sub-devices. clean slate energy ✨

Added in version 252.

-U 🌈

Shortcut for --umount --rmdir.

Added in version 252.

--attach πŸ”₯

Attaches the disk image you named to an automatically allocated loopback block device, and prints the path to that loopback block device to standard output. Think of it like running losetup --find --show --partscan, except it validates the image as a DDI before attaching, and derives the correct sector size automatically. On top of that, it makes sure the per-partition block devices are created before it returns, and it attempts to enable direct IO mode if that's possible. Takes a path to a disk image file.

If you combine it with --quiet, the output of the block device name gets suppressed. 🀫

Added in version 254.

--detach πŸ’œ

Detaches the specified disk image from a loopback block device. This undoes the effect of --attach above. Expects either a path to a loopback block device as its argument, or the path to the backing image file. In that second case it'll automatically figure out the right device to detach. iconic, honestly πŸ‘‘

Added in version 254.

--list, -l πŸ“‹

Prints the paths of all the files and directories in the specified OS image or directory to standard output.

Added in version 253.

--mtree 🌳

Generates a BSD mtree(8) compatible file manifest of the specified disk image or directory. Super useful for comparing image contents in detail, including inode information and other metadata. The generated manifest will contain detailed inode information, but rn it excludes extended attributes, file system capabilities, MAC labels, chattr(1) file flags, btrfs(5) subvolume information, and various other file metadata. File content information is shown via a SHA256 digest. Additional fields might get added in future. πŸ‘€

heads up: inode information like link counts, inode numbers and timestamps is excluded from the output on purpose, because it typically complicates reproducibility.

Added in version 253.

--with 🧚

Runs the command you specify with the OS image you specify mounted. It mounts the image to a temporary directory, switches the current working directory to it, and invokes the command line you gave as a child process. Once that process ends, it unmounts the image again and removes the temporary directory. If you don't specify a command, a shell gets invoked. 🐚

The image is mounted writable β€” use --read-only if you want to switch to read-only operation.

The invoked process will have the $SYSTEMD_DISSECT_ROOT environment variable set, containing the absolute path name of the temporary mount point, i.e. the same directory that's set as the current working directory. It'll also have the $SYSTEMD_DISSECT_DEVICE environment variable set, containing the absolute path name of the loop device the image was attached to. two env vars, both absolute, both yours πŸ’…

Added in version 253.

--copy-from, -x πŸ“€

Copies a file or directory from the specified OS image or directory into the specified location on the host file system. Expects three arguments: a path to an image file or directory, a source path (relative to the image's root directory), and a destination path (relative to the current working directory, or an absolute path β€” both outside of the image).

If the destination path is omitted or specified as a dash (-), the specified file gets written to standard output.

If the source path in the image file system refers to a regular file, it's copied to the destination path. In that case, access mode, extended attributes and timestamps get copied as well, but file ownership does not. 🚫

If the source path in the image refers to a directory, it's copied to the destination path, recursively with all containing files and directories, merging into existing directories and updating already existing files. In this case, file ownership is copied too. ✨

Added in version 247.

--copy-to, -a πŸ“₯

Copies a file or directory from the specified location in the host file system into the specified OS image or directory. Expects three arguments: a path to an image file or directory, a source path (relative to the current working directory, or an absolute path β€” both outside of the image), and a destination path (relative to the image's root directory).

If the source path is omitted or specified as a dash (-), the data to write is read from standard input.

If the source path in the host file system refers to a regular file, it's copied to the destination path. In that case, access mode, extended attributes and timestamps get copied as well, but file ownership is not.

If the source path in the host file system refers to a directory, it's copied to the destination path, recursively with all containing files and directories, merging into existing directories and updating already existing files. In this case, file ownership is copied too. (yes the original really has two periods there, we don't fix, we preserve πŸ’…)

As with --mount, file system checks are implicitly run before the copy operation begins.

Added in version 247.

--make-archive πŸ“¦

Generates an archive file from the specified disk image. Expects two arguments: the path to the disk image, and optionally the output archive file path. If that second one is omitted, the archive is written to standard output.

The archive file format gets determined automatically from the output archive file name you specified β€” e.g. any path suffixed with .tar.xz results in an xz compressed UNIX tarball (and if the path is omitted, an uncompressed UNIX tarball is created). See libarchive(3) for a list of supported archive formats and compression schemes.

Added in version 256.

--discover πŸ”

Shows a list of DDIs in well-known directories. It'll show machine, portable service and system/configuration extension disk images in the usual directories /usr/lib/machines/, /usr/lib/portables/, /usr/lib/confexts/, /var/lib/machines/, /var/lib/portables/, /var/lib/extensions/ and so on. the usual suspects πŸ•΅οΈ

Added in version 253.

--validate βœ…

Validates the partition arrangement of a disk image (DDI), and makes sure it matches the image policy specified via --image-policy=, if you specified one. It parses the partition table and probes the file systems in the image, but it does not attempt to mount them (nor to set up disk encryption/authentication via LUKS/Verity). It does all this taking the configured image dissection policy into account.

And here's the slay part: since this operation doesn't mount file systems, this command β€” unlike all the other commands implemented by this tool β€” requires no privileges other than the ability to access the specified file. πŸ‘‘

It prints OK and returns zero if the image appears to be in order and matches the specified image dissection policy. Otherwise it prints an error message and returns non-zero.

Added in version 254.

--shift πŸ”€

Recursively iterates through all inodes of the specified image and shifts the UIDs and GIDs those inodes are owned by into the specified UID range. Takes an image path and a UID base as parameters.

The UID base can be specified numerically β€” in which case it must be a multiple of 65536, and either 0 or within the container or foreign UID range, as per Users, Groups, UIDs and GIDs on systemd Systems[2] β€” or as the symbolic identifier "foreign", which is shorthand to the foreign UID base.

This command is useful for preparing directory container images for unprivileged use. Note that this command is intended for images that use the 16bit UIDs/GIDs range only, and it always ignores the upper 16bit of the current UID/GID ownership, combining the lower 16 bit with the target UID base.

Use:

systemd-dissect --shift /some/container/tree foreign

to shift a container image into the foreign UID range, or:

systemd-dissect --shift /some/container/tree 0

to shift it to host UID range. 🫢

Added in version 258.

-h, --help πŸ™‹

Prints a short help text and exits.

--version ⭐

Prints a short version string and exits.


  • --read-only, -r β€” bestie, this one locks everything down. By default --mount gives you writable mount points, but if you pass this, they get set up read-only instead. ✨ (Added in version 247.)
  • --fsck=no β€” turns off the automatic file system checking. Normally, when an image is accessed for writing (that's via --mount or --copy-to) the file systems inside the OS image get auto-checked with the appropriate fsck(8) command, in automatic fixing mode. Pass this and nope, no checking. πŸ’… (Added in version 247.)
  • --growfs=no β€” turns off the automatic growing of accessed file systems up to their partition size, when the GPT partition table says they should be. By default, when an image is accessed for writing (again: --mount or --copy-to) the file systems in the OS image get automatically grown to their partition sizes β€” but only if bit 59 in the GPT partition flags is set, for partition types defined by the UAPI.2 Discoverable Partitions Specification[1]. This behavior may be switched off using --growfs=no.

File systems are grown automatically on access if all of these are true, no cap:

  1. The file system is mounted writable
  2. The file system currently is smaller than the partition it is contained in (and thus can be grown)
  3. The image contains a GPT partition table
  4. The file system is stored on a partition defined by the Discoverable Partitions Specification
  5. Bit 59 of the GPT partition flags for this partition is set, as per specification
  6. The --growfs=no option is not passed.

(Added in version 249.)

  • --mkdir β€” if you pair it with --mount, the directory you're mounting the OS image to gets created if it's missing. πŸŽ€ But heads up: the directory is not automatically removed when the disk image is unmounted again. (Added in version 247.)
  • --rmdir β€” if you pair it with --umount, the directory where the OS image is mounted gets removed after unmounting. 🧹 (Added in version 252.)
  • --discard= β€” takes one of "disabled", "loop", "all", "crypto". If "disabled", the image is accessed with empty block discarding turned off. If "loop", discarding is enabled if operating on a regular file. If "crypt", discarding is enabled even on encrypted file systems. If "all", discarding is unconditionally enabled. (Added in version 247.)
not me clocking that the value list says "crypto" and then the very next sentence says "crypt" πŸ’€ that is genuinely, character-for-character, what the page says. I am not touching it. 🫢
  • --in-memory β€” if you pass this, an in-memory copy of the disk image you specified gets used. This is how you operate with write-access on a (possibly read-only) image without actually modifying the original file. It also lets you operate on a disk image without keeping the originating file system busy, so that file system can be unmounted. 🌈 (Added in version 253.)
  • --root-hash=, --root-hash-sig=, --verity-data= β€” these three configure various aspects of Verity data integrity for the OS image.
    • --root-hash= specifies a hex-encoded top-level Verity hash to use for setting up the Verity integrity protection.
    • --root-hash-sig= specifies the path to a file containing a PKCS#7 signature for the hash. This signature is passed to the kernel during activation, which will match it against signature keys available in the kernel keyring.
    • --verity-data= specifies a path to a file with the Verity data to use for the OS image, in case it is stored in a detached file.

It is recommended to embed the Verity data directly in the image, using the Verity mechanisms in the UAPI.2 Discoverable Partitions Specification[1]. πŸ’œ (Added in version 247.)

  • --usr-hash=, --usr-hash-sig= β€” same energy as --root-hash= and --root-hash-sig=, but for the /usr/ partition instead. Important: you use either the options for the root partition or the ones for the /usr/, but not both in combination. Pick a lane bestie. 😀 (Added in version 258.)
  • --loop-ref= β€” sets the "reference" string the kernel shall report as the backing file for the loopback block device. It's supposed to be a path or filename referencing the backing file, but that's not enforced β€” the kernel accepts arbitrary free-form strings, chosen by you. Accepts arbitrary strings up to a length of 63 characters. This sets the kernel's .lo_file_name field for the block device.

Don't get it twisted though: this is distinct from the /sys/class/block/loopX/loop/backing_file attribute file, which always reports a path referring to the actual backing file. That one is subject to mount namespace translation β€” this one is not.

This is especially iconic in combination with the --attach command, because it lets you reference the allocated loop device later via /dev/disk/by-loop-ref/... symlinks. Example: first set up the loopback device via systemd-dissect attach --loop-ref=quux foo.raw, and then reference it in a command via the specified filename: cfdisk /dev/disk/by-loop-ref/quux. πŸ”₯ (Added in version 254.)

  • --loop-ref-auto β€” like --loop-ref=, but it automatically derives the reference string from the backing filename you gave, truncating it if necessary. (Added in version 258.)
  • --mtree-hash=no β€” if you pair it with --mtree, this turns off inclusion of file hashes in the mtree output. Which makes --mtree faster when you're operating on large images. ⚑ (Added in version 254.)
  • --copy-ownership= β€” controls whether file ownership (user and group) is preserved when copying files with --copy-from or --copy-to. Takes a boolean, or auto.
    • If "yes", ownership is always preserved.
    • If "no", ownership is never preserved and the current user's UID/GID is used instead.
    • If not specified or auto, ownership is preserved when copying directory trees, but not when copying individual regular files.

(Added in version 260.)

  • --system, --user β€” when used together with --discover, these control whether to search for images installed system-wide or in the user's directories in $HOME. If neither switch is specified, it searches within both scopes. 🌸 (Added in version 258.)
  • --all β€” if you combine it with --discover, it also shows images that start with a dot, i.e. hidden images. πŸ‘€ (Added in version 258.)
  • --quiet, -q β€” if you combine it with --attach, it suppresses output of the used loopback block device path. 🀫 (Added in version 258.)
  • --image-policy=policy β€” takes an image policy string as argument, as per systemd.image-policy(7). The policy is enforced when operating on the disk image specified via --image=, see above. If not specified, defaults to the "*" policy, i.e. all recognized file systems in the image are used.
  • --image-filter=filter β€” takes an image filter string as argument, as per systemd.image-filter(7). The filter is taken into consideration when operating on the disk image specified via --image=, see above. If not specified no filtering is applied.
  • --no-pager β€” do not pipe output into a pager. πŸ™…
  • --no-legend β€” do not print the legend, i.e. column headers and the footer with hints.
  • --json=MODE β€” shows output formatted as JSON. Expects one of "short" (for the shortest possible output without any redundant whitespace or line breaks), "pretty" (for a pretty version of the same, with indentation and line breaks) or "off" (to turn off JSON output, the default). ✨

πŸ’« EXIT STATUS πŸ’«

On success, 0 is returned, a non-zero failure code otherwise. And if you used the --with command, the exit status of the invoked command is propagated. So basically whatever that inner command did, that's the tea you're getting back. πŸ«–


πŸ¦‹ INVOCATION AS /SBIN/MOUNT.DDI πŸ¦‹

Plot twist: the systemd-dissect executable may be symlinked to /sbin/mount.ddi. If it's invoked through that, it implements mount(8)'s "external helper" interface for the (pseudo) file system type "ddi". Which means conformant disk images may be mounted directly via:

# mount -t ddi myimage.raw targetdir/

in a fashion mostly equivalent to:

# systemd-dissect --mount myimage.raw targetdir/

Now listen β€” since a single DDI may contain multiple file systems, you should later unmount it with umount -R targetdir/, for recursive operation. Don't skip the -R, bestie. 😭

This functionality is particularly useful to mount DDIs automatically at boot via simple /etc/fstab entries. For example:

/path/to/myimage.raw /images/myimage/ ddi defaults 0 0

When invoked this way, the mount options "ro", "rw", "discard", "nodiscard" map to the corresponding options listed above (i.e. --read-only, --discard=all, --discard=disabled). Note that mount options are not generically passed on to the file systems inside the images. ✨


πŸŽ€ EXAMPLES πŸŽ€

Example 1. Generate a tarball from an OS disk image (--with)

# systemd-dissect --with foo.raw tar cz . >foo.tar.gz

or alternatively just:

Example 2. Generate a tarball from an OS disk image (--make-archive)

# systemd-dissect --make-archive foo.raw foo.tar.gz

πŸ‘‘ SEE ALSO πŸ‘‘

systemd(1), systemd-nspawn(1), systemd.exec(5), systemd.v(7), UAPI.2 Discoverable Partitions Specification[1], mount(8), umount(8), fdisk(8)


πŸ“ NOTES πŸ“

  1. UAPI.2 Discoverable Partitions Specification https://uapi-group.org/specifications/specs/discoverable_partitions_specification
  2. Users, Groups, UIDs and GIDs on systemd Systems https://systemd.io/UIDS-GIDS/

and that's the whole page bestie, we ate and left absolutely no crumbs πŸ’…βœ¨ this one's from systemd 262.1, living under the SYSTEMD-DISSECT(1) banner, and it's literally iconic. go mount something. 🫢🌸

πŸ“¦ where this page comes from
  • arch rolling: systemd 262.1-1 Β· LGPL-2.1-or-later AND CC0-1.0 AND GPL-2.0-or-later AND MIT-0
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms