Root Filesystem Integrity with dm-verity: Design Patterns for Immutable Images
How to design and implement dm-verity-protected root filesystems for embedded Linux, covering hash tree generation, kernel integration, error handling, and update strategies.

dm-verity provides transparent block-level integrity verification for read-only filesystems. Every block read from disk is checked against a pre-computed hash tree. If a block has been modified—whether by an attacker, bit rot, or a failed flash write—the read returns an error instead of corrupted data. For embedded devices with immutable system partitions, dm-verity closes the gap between secure boot and runtime integrity.
Where dm-verity fits in the security chain
Secure boot (covered in the FIT signatures guide) verifies the kernel and initramfs at boot time. But once the kernel is running and mounts the root filesystem, there is no ongoing verification of the blocks read from storage—unless you add it.
dm-verity provides that ongoing verification. The chain becomes:
- ROM → verifies SPL
- SPL → verifies U-Boot
- U-Boot → verifies kernel + initramfs (FIT signature)
- Kernel → verifies root filesystem blocks (dm-verity)
With all four stages in place, every piece of software running on the device has been cryptographically verified.
How dm-verity works
dm-verity uses a Merkle tree (hash tree) computed over the filesystem image. The tree structure:
- Data blocks: The actual filesystem content, divided into fixed-size blocks (typically 4096 bytes)
- Hash blocks: Each hash block contains the hashes of multiple data blocks
- Higher-level hash blocks: Each higher level hashes the blocks below it
- Root hash: A single hash at the top of the tree
The root hash is the trust anchor. If you know the root hash is correct (because it was signed or embedded in the verified kernel command line), dm-verity can verify every block on demand.
Generating the hash tree
Use veritysetup from the cryptsetup package:
# Create the hash tree
veritysetup format /path/to/rootfs.img /path/to/rootfs.hashtree
# Output includes:
# Root hash: 4a2c...
# Salt: 8b1f...
# Data blocks: 65536
# Hash algorithm: sha256
The root hash and salt must be recorded and passed to the kernel at boot time. Store them in the FIT image configuration or in the kernel command line.
Kernel command line integration
The simplest approach passes verity parameters on the kernel command line:
root=/dev/dm-0
dm-mod.create="verity,,,ro,0 524288 verity 1 /dev/mmcblk0p2 /dev/mmcblk0p3 4096 4096 65536 1 sha256 ROOT_HASH SALT"
Replace ROOT_HASH and SALT with the values from veritysetup format. The command line parameters specify:
- The data device (
/dev/mmcblk0p2) - The hash device (
/dev/mmcblk0p3) - Block sizes (data and hash)
- Number of data blocks
- Hash algorithm
- Root hash and salt
Since the kernel command line is part of the signed FIT image, the root hash is protected by the secure boot chain.
Initramfs-based setup (recommended)
For more flexibility, set up dm-verity in the initramfs:
#!/bin/sh
# In init script
veritysetup open /dev/mmcblk0p2 rootfs /dev/mmcblk0p3 ${ROOT_HASH}
mount /dev/mapper/rootfs /newroot
exec switch_root /newroot /sbin/init
This approach allows error handling, fallback logic, and logging before the root filesystem is mounted.
Filesystem choices for dm-verity
dm-verity requires a read-only filesystem. Common choices:
- SquashFS: Compressed, widely supported, good tool ecosystem. Typical for embedded systems with flash storage constraints.
- EROFS: Enhanced Read-Only File System with better random-read performance. See the the EROFS documentation for detailed benchmarks.
- ext4 (read-only): An ext4 image mounted read-only works with dm-verity but offers no compression. Useful when you need ext4 tooling compatibility.
Error handling strategies
When dm-verity detects a corrupted block, the default kernel behaviour is to return an I/O error to the reading process. This usually means the process crashes, which may leave the system in an unpredictable state.
Better strategies:
Restart mode
Configure dm-verity to trigger a kernel panic on corruption. Combined with a watchdog, this causes a reboot into a recovery partition:
dm-verity.error_behavior=3 # panic
Corruption logging
Log the corrupted block number to a persistent partition before rebooting. This helps diagnose whether the corruption is systematic (storage failure) or targeted (attack):
# In a verity error handler script
echo "verity corruption at block ${BLOCK}" >> /data/verity-errors.log
reboot
Forward error correction
dm-verity supports an optional FEC (Forward Error Correction) mode that can repair corrupted blocks using Reed-Solomon codes. This adds space overhead (typically 2–8% of the image size) but allows transparent recovery from minor storage corruption.
Update strategies with dm-verity
Since dm-verity protects a read-only image, updates mean replacing the entire image and hash tree. This aligns naturally with A/B update schemes:
- Download new rootfs image + hash tree to the inactive slot
- Verify the download integrity (checksum or signature)
- Update the boot configuration to point to the new slot
- Reboot
The OTA update tooling documentation covers RAUC and SWUpdate integration with dm-verity-protected partitions.
Writable overlay for runtime data
A fully read-only root filesystem is impractical for most systems. Logs, configuration changes, and runtime state need writeable storage. The standard pattern is an overlayfs mount:
mount -t overlay overlay -o lowerdir=/rootfs,upperdir=/data/upper,workdir=/data/work /merged
The lower layer is the dm-verity-protected read-only image. The upper layer is on a separate, writable partition. Changes appear in the upper layer without modifying the verified lower layer.
Performance considerations
dm-verity adds overhead to every block read. On flash-based storage (eMMC, NAND), the overhead is typically 5–15% for sequential reads and negligible for cached reads. The hash tree is small enough to fit in page cache for most embedded images.
For latency-sensitive applications, pre-populate the hash tree in page cache during boot by reading it sequentially before mounting.
Integration with ProteanOS
ProteanOS build workflows can integrate dm-verity hash tree generation as a post-build step. The ProKit documentation covers adding custom post-processing steps to the build pipeline.
For new hardware platforms, the the board bring-up documentation includes dm-verity validation as a security milestone after basic boot and secure boot are working.
Summary
dm-verity provides runtime integrity verification for read-only root filesystems. Combined with secure boot, it creates a complete chain of trust from power-on to application execution. The implementation involves generating a hash tree, passing the root hash through a verified channel (signed kernel command line or initramfs), and handling corruption gracefully. Plan your partition layout and update strategy around the read-only constraint from the start.