FreeBSD mdconfig: Build and Operate Memory Disk Devices Safely
Use FreeBSD mdconfig to attach image, swap-backed, and null memory disks, verify their GEOM providers, mount safely, and detach without losing data.
FreeBSD’s md driver presents a configured memory disk as a block device, typically named /dev/mdN. The backing store can be a regular file, memory allocated through the VM system, or a null sink. Because applications interact with a GEOM-visible provider, the device can hold a UFS filesystem, expose an ISO image, or carry a partition table just like other storage providers. The operational risks are also storage risks: a mounted image can be corrupted by concurrent writers, a RAM-backed device can consume resources unexpectedly, and an attached device remains allocated until it is detached.
The term “memory disk” is historical and can mislead. A vnode-backed md device does not copy the entire image into RAM; it presents a block interface backed by a file. A swap-backed device uses VM-managed pages that can move to swap under pressure. A malloc-backed device allocates from kernel malloc and can be especially dangerous at large sizes. Choose the backing type based on the task rather than assuming every md device is a RAM disk.
Select the backing type first
The vnode type is useful for inspecting or testing a disk image stored on a persistent filesystem. Reads and writes go through the backing vnode according to md’s options and the underlying filesystem’s behavior. Mounting the same image writable on two paths or hosts at once is not a consistency strategy: the filesystem inside the image has no knowledge of independent writers, and concurrent updates can corrupt it. Use read-only access for inspection whenever the image should remain unchanged.
The swap type creates a block provider backed by buffer memory. Pages can remain in memory or be pushed to swap during pressure. This can be appropriate for a disposable UFS filesystem needed by a test or build, but it consumes the same broad memory and swap budget as other workloads. A system that has little swap or is already under pressure should not create a large swap-backed device casually.
The malloc type uses kernel malloc allocations. The mdconfig manual warns that a large malloc-backed device can panic a system if allocation is not reserved safely. Avoid it for general temporary storage and do not treat it as interchangeable with swap backing. The null type discards writes and returns zeroes on reads, making it a specialized sink/source for tests rather than a place to store data.
Before attaching anything, identify the backing image, its size, the expected filesystem or partitioning, and whether the test may modify it. Record a checksum of a source image when image integrity matters. Confirm the target mountpoint is empty and not already mounted. On a production host, use a maintenance change record so another operator can distinguish a disposable md device from a physical provider.
Attach an existing ISO image read-only
The Handbook demonstrates attaching an ISO file as an md device, mounting it as CD9660, then unmounting and detaching it. The following uses automatic unit allocation to avoid assuming md0 is free:
set -eu
image=/var/tmp/recovery.iso
mountpoint=/mnt/recovery-iso
device=
mounted=no
cleanup() {
status=$?
trap - 0 HUP INT TERM
if [ "$mounted" = yes ]; then
if umount "$mountpoint"; then
mounted=no
else
echo "Unmount failed; leaving $device attached for investigation" >&2
device=
status=1
fi
fi
if [ -n "$device" ]; then
if mdconfig -d -u "$device"; then
device=
else
echo "Detach failed for $device; inspect active consumers" >&2
status=1
fi
fi
exit "$status"
}
trap cleanup 0
trap 'exit 1' HUP INT TERM
mkdir -p "$mountpoint"
device=$(mdconfig -a -t vnode -f "$image" -o readonly)
if ! printf '%s\n' "$device" | grep -Eq '^md[0-9]+$'; then
echo "Unexpected mdconfig device name: $device" >&2
device=
exit 1
fi
mdconfig -l -v -u "$device"
mount -t cd9660 -o ro "/dev/$device" "$mountpoint"
mounted=yes
mount | grep -F "$mountpoint"
find "$mountpoint" -maxdepth 2 -type f
umount "$mountpoint"
mounted=no
mdconfig -d -u "$device"
device=
mdconfig -l
The attach command prints the allocated device name when -u is omitted. This complete example retains that exact returned name for inspection, mount, unmount, and detach instead of guessing a unit such as md0 or copying a stale numeric unit. It assumes the image contains an ISO filesystem at the device start; for a whole-disk image with a partition table, do not run the mount line directly. Inspect the layout with gpart show "/dev/$device" and mount the actual filesystem child provider instead.
The exit trap attempts cleanup in reverse order. It detaches only the unit captured from this attach, and only after a successful unmount; if unmount fails, it leaves the provider attached and reports that state rather than forcing teardown. If detach reports the provider is busy, inspect mounts, open descriptors, swap, GEOM consumers, and other processes holding it. Do not add the force option merely to clear an error: an in-use device is evidence that teardown is incomplete, and force can invalidate consumers that still depend on it. After an interrupted run, inspect mdconfig -l -v and the mount table before any manual cleanup; never detach a unit based only on an old example.
Create a disposable swap-backed UFS volume
For a temporary filesystem, mdconfig can allocate a fixed-size swap-backed device. Use a size chosen for the test, not an arbitrary large number:
mdconfig -a -t swap -s 1g
mdconfig -l -v
After attaching, note the returned md unit. The following operations are destructive to that new provider and must be run only after confirming its identity:
newfs /dev/md4
mkdir -p /mnt/md-lab
mount /dev/md4 /mnt/md-lab
df -h /mnt/md-lab
This writes a new UFS filesystem to md4. Do not copy the sample unit into a system with an existing md4 device. Verify the provider and its backing details with mdconfig -l -v before running newfs. For an automated test, record mdconfig -l -v before and after the run, and ensure the cleanup path is exercised even after a failed test.
The device capacity is not equivalent to an equal quantity of permanently reserved physical RAM. It is still finite, and the VM system may place backing pages into swap. If the host is already paging heavily, adding a large md swap device can intensify pressure instead of improving throughput. Monitor swapinfo, vmstat, and application response time during a representative test.
The filesystem layer has its own accounting. df -h reports the UFS filesystem’s view, not all host resources held by the md provider. UFS metadata and reserved blocks also reduce what an application can write. Do not fill the volume to 100 percent and assume the kernel, test harness, and cleanup procedure will still have enough space to finish normally.
Use a disk image as an inspectable provider
Attaching a disk image creates a useful boundary between the image file and GEOM. The provider may contain a raw filesystem directly or a partition table with child providers. Use gpart show /dev/mdN to inspect a partitioned image before mounting anything. Device discovery can lag or differ if the image is malformed, truncated, or uses a partitioning scheme unsupported by the running kernel.
For forensic or recovery work, preserve the source image and operate on a copy. If the image must not be changed, use md’s read-only option and also mount the detected filesystem read-only. This defense-in-depth approach prevents accidental filesystem writes through a normal mount path, but it does not prove that every tool is read-only. Some repair utilities modify metadata unless invoked in their documented no-write mode. Check each utility’s manual and maintain a verified copy.
For a writable test, copy the image to a working location with enough free space, record its checksum, attach the copy, then record the output and any changed data. A successful mount only proves that the kernel can interpret enough of the image to mount it. It does not establish that the image is complete, that all files are readable, or that a later write remains within the image’s allocated host storage. Validate expected files and checksums after unmounting, then compare the resulting image digest if modifications were expected.
The vnode backing file lives on an ordinary filesystem, so the md device can become I/O-bound by that filesystem or its physical storage. Caching and asynchronous vnode options have tradeoffs. In particular, the manual documents a vnode async mode that can risk deadlocking the kernel; it should not be enabled as a generic performance switch. Keep default behavior unless a tested use case and the installed mdconfig(8) manual justify changing it.
Lifecycle, inventory, and failure handling
Treat attachment and detachment as a paired resource lifecycle. A script should capture the allocated unit, remember the mountpoint, and clean up in reverse order: stop processes using the filesystem, unmount, then detach the provider. If setup fails after attach but before mount, still detach the unit. If the mount fails because the wrong filesystem type was selected, do not run newfs on the image to “make it work”; first identify whether the source is an ISO, raw partition, whole-disk image, or corrupt file.
Inventory commands distinguish configured devices from mounted filesystems. mdconfig -l -v lists md devices and detailed backing data. mount shows mounted filesystems, while gpart show helps inspect partitioned providers. df reports filesystem use. None of these views alone answers every question. A listed md unit may not be mounted; a mounted filesystem may sit on a partition child; an open descriptor can keep the provider busy after the mount has been removed.
Common failures point to different layers. “No such file” may mean a missing image or an incorrect unit. “Operation not permitted” usually indicates privilege or policy. A mount failure can be the wrong filesystem type or a damaged image. newfs errors may mean the device is too small or is not the intended blank provider. An EBUSY detach indicates a live consumer. Preserve the exact error and inspect current state before issuing another mutating command.
Use a disposable VM for destructive experiments, particularly for partition tables and filesystem creation. A useful acceptance test attaches one known image, verifies that the resulting provider points to it, mounts the expected filesystem read-only, reads known files, then unmounts and detaches cleanly. For swap-backed scratch storage, fill only a bounded test dataset, monitor VM pressure, and confirm cleanup removes both mount and md provider. The result is a repeatable image workflow instead of an orphaned device or overwritten source artifact.
Related:
- Understanding GEOM: FreeBSD’s Modular Storage Framework
- FreeBSD CAM Storage Diagnostics: Tracing Devices from Bus to GEOM
Sources: