How to Use dm-verity on Linux: A Complete and Practical Guide

  • dm-verity verifies blocks on the fly with a signed root hash tree, anchoring the boot chain of trust.
  • Its modern deployment combines veritysetup, systemd-veritysetup, Secure Boot, and UKI to protect the kernel, initramfs, and cmdline.
  • Android uses system-as-root and AVB to pass dm-verity parameters; FEC and reaction policies enhance robustness.
  • The immutable root requires separating writable data (/var, /home) and scheduling updates using images or A/B schemes.

dm-verity on Linux

If you are concerned about the integrity of your system, dm-verity is one of the key pieces of the Linux ecosystem to boot safely and detect storage tampering. It originated as part of the kernel's device mapper and is now the basis for verified booting in Android, OpenWrt, and distributions seeking enhanced security.

Far from being an abstract concept, dm-verity is configured and used with real tools like veritysetup and systemd-veritysetupIt validates blocks on the fly using hash trees and can react to corruption with policies ranging from logging the event to rebooting or crashing the system. Let's take a closer look, without leaving any loose ends.

What is dm-verity and why you might care

Integrity verification with dm-verity

dm-verity is a device-mapper target in the kernel that verifies the integrity of a block device as data is readIt works by calculating and verifying hashes of each block (usually 4K) against a pre-computed hash tree, typically using SHA-256.

This design allows for Files cannot be silently modified between reboots or during executionIt's key to extending the boot chain of trust to the operating system, limiting malware persistence, strengthening security policies, and ensuring encryption and MAC mechanisms during boot.

On Android (since 4.4) and Linux in general, Trust is anchored in the root hash of the tree, which is signed and validated with a public key located in a protected location (e.g., on the boot partition or in a Secure Boot-signed UKI). Breaking any block would require breaking the underlying cryptographic hash.

Verification is done by block and on demand: The added latency is minimal compared to the I/O costIf a check fails, the kernel returns an I/O error and the file system appears corrupted, which is expected when the data is unreliable. Apps can decide whether to continue or not based on their fault tolerance.

How the verification tree works internally

The verification tree is built in layers. Layer 0 is the raw data from the device, divided into 4K blocks; a SHA-256 (salted) hash is calculated for each block. These hashes are then concatenated to form layer 1. Layer 1 is then grouped into blocks and rehashed to form layer 2, and so on until everything fits into a single block: that block, when hashed, produces the root hash.

If any layer does not exactly complete a block, It is padded with zeros until it reaches 4K to avoid ambiguity. The total size of the tree depends on the size of the partition being checked; in practice, it's usually less than 30 MB for typical system partitions.

The general process is: choose a random salt, hash to 4K, calculate SHA-256 with per-block salt, concatenates to form levels, pads the block boundary with zeros, and repeats with the previous level until a single root hash is left. That root hash, along with the salt used, feeds the dm-verity table and the signature.

Disk format versions and algorithm

The format of hash blocks on disk has a version. Version 0 was the original version used in Chromium OS: The salt is added at the end of the hashing process, digests are stored continuously, and the rest of the block is padded with zeros.

La Version 1 is recommended for new devices: The salt is prepended to the hash, and each digest is padded with zeros up to powers of two, improving alignment and robustness. The dm-verity table also specifies the algorithm (e.g., sha1 or sha256), although for current security, sha256 is used.

dm-verity table and essential parameters

The target table dm-verity describes where the data is, where the hash tree is, and how to verifyTypical table fields:

  • giant: device with the data to be verified (path type /dev/sdXN or greater:lesser).
  • hash_dev: device with the hash tree (can be the same; if so, hash_start must be outside the checked range).
  • data_block_size: data block size in bytes (e.g. 4096).
  • hash_block_size: hash block size in bytes.
  • num_data_blocks: number of verifiable data blocks.
  • hash_start_block: offset (in hash_block_size blocks) to the root block of the tree.
  • algorithm: hash algorithm (e.g. sha256).
  • digest: hexadecimal encoding of the root block hash (including salt according to format version); this value is the one to trust.
  • salt: hexadecimal salt.

In addition, there are optional parameters very useful for adjusting behavior:

  • ignore_corruption: Records corrupt blocks, but allows reading to continue.
  • restart_on_corruption: restart on corruption detection (not compatible with ignore_corruption and requires user-space support to avoid loops).
  • panic_on_corruption: : causes panic when detecting corruption (not compatible with previous versions).
  • restart_on_error y panic_on_error: same reactions but for I/O errors.
  • ignore_zero_blocks: does not check blocks that are expected as zeros and returns zeros.
  • use_fec_from_device + fec_roots + fec_blocks + fec_start: Enable Reed–Solomon (FEC) to recover data when verification fails; the data, hash, and FEC areas must not overlap, and the block sizes must match.
  • check_at_most_once: Checks each block of data only the first time it is read (reduces overhead at the cost of security in live attacks).
  • root_hash_sig_key_desc: Reference to a key in the keyring to validate a PKCS7 signature of the root hash when creating the mapping (requires appropriate kernel configuration and trusted keyrings).
  • try_verify_in_tasklet: If hashes are cached and I/O size allows, checks bottom-half to reduce latency; adjusted with /sys/module/dm_verity/parameters/use_bh_bytes per I/O class.

Signature, metadata and trust anchoring

For dm-verity to be reliable, The root hash must be trusted and usually signedIn classic Android, a public key is included in the boot partition, which is externally verified by the manufacturer; it validates the root hash signature and ensures that the system partition has not been altered.

Verity metadata adds structure and version control. The metadata block includes a magic number 0xb001b001 (bytes b0 01 b0 01), version (currently 0), the table signature in PKCS1.5 (typically 256 bytes for RSA-2048), the table length, the table itself, and zero padding up to 32K.

In Android implementations, verification relies on fs_mgr and fstab: Adding a check mark to the corresponding entry and placing the key in /boot/verity_key. If the magic number isn't where it should be, verification stops to avoid checking the wrong thing.

Start operation verified

Protection lives in the kernel: If compromised before the kernel boots, the attacker retains controlThat's why manufacturers typically strictly validate each stage: a key burned into the device verifies the first bootloader, which verifies the next, the app bootloader, and finally, the kernel.

With the kernel verified, dm-verity is enabled when mounting the verified block deviceInstead of hashing the entire device (which would be slow and waste energy), it's verified block by block as it's accessed. A failure causes an I/O error, and services and apps react according to their tolerance: either continuing without that data or crashing completely.

Forward Error Correction (FEC)

Since Android 7.0, FEC (Reed–Solomon) is incorporated with interlacing techniques to reduce space and increase the ability to recover damaged blocks. This works in conjunction with dm-verity: if a check fails, the subsystem can attempt to correct it before declaring it unrecoverable.

Performance and optimization

To reduce impact: Enable SHA-2 acceleration by NEON on ARMv7 and SHA-2 extensions on ARMv8 from the kernel. Adjust read-ahead and prefetch_cluster parameters for your hardware; per-block verification typically adds little to the I/O cost, but these settings make a difference.

Getting started on Linux (systemd, veritysetup) and Android

Configuring dm-verity on Linux and Android

On a modern Linux with systemd, dm-verity allows a verified read-only root using veritysetup (part of cryptsetup), systemd-veritysetup.generator, and systemd-veritysetup@.service. It's recommended to include Secure Boot and a signed UKI (unified kernel image), although they're not strictly required.

Preparation and recommended partitioning

Part of a functional and adjusted system. Reserve a volume for the hash tree (8–10% of root size is usually enough) and consider separating /home and /var if you need to write. A typical scheme includes: ESP (for the bootloader), XBOOTLDR (for UKIs), root (with or without encryption), VERITY partition, and optionally /home and /var.

As a root, EROFS is a very interesting alternative to ext4 or squashfs: It is read-only by design, with very good performance on flash/SSD, lz4 compression by default, and widely used on Android phones with dm-verity.

Files that must be writable

With root ro, some programs expect to write to /etc or during initYou can move it to /var/etc and symlink anything that needs to change (e.g., NetworkManager connections in /etc/NetworkManager/system-connections). Note that systemd-journald requires /etc/machine-id to exist in the root directory (not a symlink) to avoid breaking early startups.

To find out what changes in execution, use dracut-overlayroot: overlays a tmpfs over the root, and everything written appears in /run/overlayroot/u. Add the module to /usr/lib/dracut/modules.d/, include overlayroot in dracut, and set overlayroot=1 on the kernel line; this way you'll see what to migrate to /var.

Useful examples: pacman and NetworkManager

In Arch, it is convenient Move the Pacman database to /usr/lib/pacman so that the rootfs always mirrors installed packages. Then, redirect the cache to /var/lib/pacman and link. To change the mirrorlist without touching the root, move it to /var/etc and link it anyway.

With NetworkManager, move system-connections to /var/etc/NetworkManager and link from /etc/NetworkManager/system-connections. This keeps the root immutable and the configuration live where it should be writable.

Construction of verity and testing

From a live and with everything perfect and mounted in ro, create the tree and roothash with veritysetup format: When run, it prints the Root Hash line, which you can save to roothash.txt. Run it for testing with veritysetup open root-device root verity-device $(cat roothash.txt) and mount /dev/mapper/root.

If you prefer, first generates the tree to a file (verity.bin) and then write it to the VERITY partition. The resulting set is: root image, verity tree, and the root hash you'll pin at boot.

Configure the kernel line

Add these parameters: systemd.verity=1, roothash=contents_of_roothash.txt, systemd.verity_root_data=ROOT-PATH (e.g. LABEL=OS), and systemd.verity_root_hash=VERITY-PATH (e.g. LABEL=VERITY). Set systemd.verity_root_options to restart-on-corruption or panic-on-corruption for strict policies.

Other recommended options: ro (if you don't use EROFS/squashfs), rd.emergency=reboot y rd.shell=0 (prevent unauthorized shells if boot fails), and lockdown=confidentiality to protect kernel memory from access.

Additional partitions with verity

Not just the root: You can define other mappings in /etc/veritytab and systemd-veritysetup@.service will assemble them at boot. Remember: it's easier to RW mount a non-root partition, and a root user could disable Verity on those partitions, so the security value there is lower.

Security: Secure Boot, UKI and signed modules

dm-verity is not a silver bullet. Sign the UKI and enable Secure Boot with your own keys to prevent anyone from overriding kernel/initramfs/cmdline (which includes the root hash). Tools like sbupdate-git or sbctl help keep images signed and the boot chain intact.

If you enable kernel lockdown or module signature verification, DKMS or out-of-tree modules must be signed or they won't load. Consider a custom kernel with signing support for your pipeline (see signed kernel modules).

Encryption, TPM and metering

dm-verity protects integrity, non-confidentialityYou can leave root unencrypted if it doesn't contain secrets and the boot chain is protected. If you use keyfiles from root to unlock other volumes, then it's a good idea to encrypt it.

With TPM 2.0, systemd-cryptenroll allows binding keys to PCRs 0,1,5,7 (firmware, options, GPT, secure boot status). Add rd.luks.options=LUKS_UUID=tpm2-device=auto and make sure to include TPM2 support in the initramfs. systemd-boot measures the kernel.efi in PCR4, useful for invalidating keys if the UKI or its cmdline changes.

Updates and deployment models

A verified read-only root It is not updated with the package manager in the traditional way. The ideal is to build new images with tools like the Yocto project and publish them. systemd has systemd-sysupdate and systemd-repart for robust image downloading and flashing.

Another strategy is A/B scheme: You keep two roots and two verities. Copy the active root to the inactive root, apply changes, and redo verity. Switch back on the next boot. If you're using UKI, remember to update the root hash in the cmd line or rebuild the signed UKI.

For optional persistence, use OverlayFS on the verified root with upper in tmpfs or disk. You can also pass systemd.volatile=overlay for temporary persistence. Flatpak makes it easy to install apps in /var and /home without touching /.

There are automated packages (e.g. verity-squash-root in AUR) that build a squashfs root and sign the roothash with kernel and initramfs, allowing you to choose between persistent or ephemeral mode and preserving the latest rootfs as a backup. Note: adding persistence to a verified root has narrow use cases; try persisting app data on separate partitions.

Android: system-as-root, AVB and vendor overlays

Since Android 10, RootFS stops running on RAM disk and integrates with system.img. (system-as-root). Devices launching with Android 10 always use this scheme and require a ramdisk for dm-linear. BOARD_BUILD_SYSTEM_ROOT_IMAGE is set to false in this build to distinguish between using a ramdisk and directly activating system.img.

Android 10 incorporates dynamic partitions and a first-stage init which activates the logical system partition; the kernel no longer mounts it directly. System-only OTAs require a system-as-root design, which is mandatory on Android 10 devices.

In no A/B, keep recovery separate from bootUnlike A/B, there is no boot_a/boot_b backup, so removing recovery in non-A/B can leave you without recovery mode if a boot update fails.

The kernel mounts system.img to /converity via two paths: vboot 1.0 (patches for the kernel to parse Android metadata in /system and derive dm-verity parameters; the cmdline includes root=/dev/dm-0, skip_initramfs and init=/init with dm=…) or vboot 2.0/AVB, where the bootloader integrates libavb, reads the hashtree descriptor (in vbmeta or system), constructs the parameters and passes them to the kernel in the cmdline, with FEC support and flags like restart_on_corruption.

With system-as-root, don't use BOARD_ROOT_EXTRA_FOLDERS for device-specific root folders: these will disappear when flashing a GSI. Define specific mounts under /mnt/vendor/ , which fs_mgr creates automatically, and reference them in the fstab of the device tree.

Android allows a vendor overlay from /product/vendor_overlay/: init will mount in /vendor the subdirectories that meet the SELinux context requirements and the existence of /vendor/ . Requires CONFIG_OVERLAY_FS=yy, on older kernels, the override_creds=off patch.

Typical implementation: installs precompiled files in device/ / /vendor_overlay/, add them to PRODUCT_COPY_FILES with find-copy-subdir-files to $(TARGET_COPY_OUT_PRODUCT)/vendor_overlay, define contexts in file_contexts for etc and app (e.g. vendor_configs_file and vendor_app_file) and allow mounton on those contexts in init.te. Test with atest vfs_mgr_vendor_overlay_test in userdebug.

Troubleshooting: dm-verity corruption message on Android

On devices with A/B slots, change slots or Flashing vbmeta/boot without consistency with the roothash This may trigger the warning: dm-verity corruption, your device is untrusted. Commands like fastboot flash –disable-verity –disable-verification vbmeta vbmeta.img disable verification, but leave the system without any guarantees of integrity.

Some bootloaders support fastboot oem disable_dm_verity and its opposite, enable_dm_verity. It works on some models, but not on others; and it may require kernel/magisk with adjusted flags. Use at your own risk: the prudent course of action is align boot, vbmeta, and system, sign or regenerate the tree and ensure that the expected root hash matches the configured one.

If after the warning you can continue pressing power, the system starts, but you no longer have an intact chain of trustTo remove the message without sacrificing security, restore the original signed images or rebuild/verify vbmeta with the correct hashtree, instead of disabling verity.

i.MX and OpenWrt platforms

On i.MX6 (e.g. sabresd), configure the kernel with DM_VERITY and FEC support, generate the tree with veritysetup, store the root hash securely, and pass the appropriate parameters in the cmd line or integrate via initramfs with systemd-veritysetup. If you don't use dm-crypt, you don't need CAAM for verity; the focus is on integrity.

In OpenWrt and in embedded Linux systems with OpenEmbedded, There are efforts to integrate dm-verity and SELinux (Bootlin jobs revised with the intention of incorporating support). It's a natural fit: routers and network equipment benefit from an immutable, verified, and MAC-hardened root.

Manual tree and metadata construction (detailed view)

cryptsetup can generate the tree for you, but if you prefer to understand the format, the compact table line definition includes: mapping name, data device, data block and hash sizes, image size in blocks, hash_start position (block image + 8 if concatenated), root hash, and salt. After generating the concatenated layers (from top to bottom, excluding layer 0), you write the tree to disk.

To pack everything, compose the dm-verity table, sign it (typical RSA-2048) and group signature+table in metadata with a versioned header and magic number. Then, it concatenates the system image, verity metadata, and hash tree. In fstab, it marks fs_mgr as verify and places the public key in /boot/verity_key to validate the signature.

Optimize with SHA-2 speedups for your CPU and adjust read-ahead/prefetch_cluster. On ARM hardware, NEON SHA-2 (ARMv7) and SHA-2 extensions (ARMv8) significantly reduce the verification overhead.

In any deployment, remember that the root hash value must be protected: whether compiled into a signed UKI, into the signed boot partition, or validated by the bootloader using AVB. Everything after that point inherits that trust.

With all of the above in place, dm-verity becomes a solid foundation for immutable, mobile and embedded systems, supporting transactional updates, configuration overlays, and a modern security model that reduces the attack surface and prevents persistence without sacrificing performance.

What is the Yocto project?
Related article:
What is the Yocto Project: A Complete Embedded Guide