Managing Device Nodes with mdev
A hands-on guide to devtmpfs and BusyBox mdev in a free embedded Linux course
free linux device drivers course
devtmpfs
mdev
device nodes
Every embedded Linux root filesystem needs a working /dev directory before anything useful can happen: without device nodes, your init process cannot open a console, mount a real root filesystem, or talk to storage. In this lecture of our free embedded Linux course we look at the two practical ways a minimal root filesystem populates /dev at boot — the kernel’s own devtmpfs filesystem, and BusyBox’s lightweight hotplug manager, mdev. This is a direct continuation of the daemons and device-node overview from the previous lecture, and here we get our hands dirty configuring both.
What You Will Learn
Configuring mdev as a hotplug client
Writing /etc/mdev.conf rules
The modern netlink-based mdev daemon mode
When static device nodes still make sense
Prerequisites
You should already have a staged root filesystem with BusyBox built in (covered earlier in this free embedded systems course), and understand what /proc and /sys are used for. A QEMU or hardware target that boots your rootfs from the previous lectures is assumed.
Why /dev Cannot Just Be a Static Directory
A device node is nothing more than an inode carrying a major and minor number, so in principle you could create every node you will ever need with mknod ahead of time and ship them baked into the image. That approach works, but it breaks the moment your hardware changes: plug in a USB drive, insert an SD card, or bind a new i2c device at runtime, and a statically populated /dev has no way to react. Modern embedded Linux systems solve this with two cooperating pieces: the kernel creates and destroys device nodes automatically through devtmpfs, and a small userspace helper like mdev layers ownership, permission, and naming policy on top.
Enabling and Mounting devtmpfs
devtmpfs is a kernel-managed, RAM-backed filesystem that the driver core populates directly: whenever a driver calls device_create(), the kernel creates the matching node in devtmpfs itself, with no userspace round trip required. This is what lets a system boot to a usable console even before any hotplug daemon has started. Two kernel config options control it:
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
With CONFIG_DEVTMPFS_MOUNT enabled, the kernel mounts devtmpfs on /dev automatically during early boot, before your init script even runs — so on most modern builds you do not need to mount it by hand at all. If you are working with an older kernel config, or want to be explicit in your init scripts, you can still mount it manually:
# mount -t devtmpfs devtmpfs /dev
For a permanent setup, the mount belongs early in your init sequence, alongside proc and sysfs:
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
|
v
device_create() called by driver
|
v
devtmpfs core creates /dev/ttyS0, /dev/sda, etc.
|
v
Node exists with default 0600 root:root
|
v
mdev (if running) applies /etc/mdev.conf policy
|
v
Correct permissions / ownership / symlinks applied
devtmpfs alone gives every node the same default owner and mode, which is usually too permissive or too restrictive depending on the device. That policy layer is exactly what mdev adds.
Setting Up mdev as a Hotplug Client
mdev has two jobs: an initial scan of everything already present in /sys at boot, and ongoing handling of devices that appear or disappear afterward. The scan is triggered with -s:
# mdev -s
This walks /sys/class and /sys/block, and for every device it finds, creates the corresponding node in /dev according to the rules in /etc/mdev.conf. The classic way to make mdev react to devices appearing later is to register it as the kernel’s hotplug helper:
echo /sbin/mdev > /proc/sys/kernel/hotplug
This tells the kernel to fork and exec /sbin/mdev for every uevent, passing device details as environment variables. It works, but on a busy system — think a USB hub with several devices attaching at once — forking a fresh process per event adds up. Recent BusyBox releases address this with a persistent daemon mode that listens directly on the kernel’s netlink uevent socket instead of relying on the legacy hotplug helper:
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mdev -s
mdev -daemon
With mdev -daemon running, there is a single long-lived process subscribed to netlink events, which is both faster and lighter than the fork-per-event hotplug path. Prefer this on any recent BusyBox build; fall back to the /proc/sys/kernel/hotplug method only if your BusyBox is too old to include daemon mode.
Writing mdev.conf Rules
Without a config file, every node mdev creates gets the default 0:0 660 — root-owned, group-writable. Real devices usually need different permissions. Rules live in /etc/mdev.conf, one per line, in the form:
[-]DEVNAME UID:GID PERM [=PATH|>PATH] [@|$|*COMMAND]
A practical starting point that fixes the permissions on the RNG and null devices:
null root:root 666
random root:root 444
urandom root:root 444
console root:tty 600
mdev matches lines top to bottom and stops at the first match, so put your most specific rules first and a catch-all default last if you need one. You can also move or rename nodes as they are created — useful when a driver’s default name does not match the layout your applications expect, for example filing block devices under a drives/ subdirectory instead of directly in /dev.
Are Static Device Nodes Ever the Right Call?
Statically created nodes — baked into the rootfs image ahead of time with mknod or a device table — have one genuine advantage: zero boot-time cost, since there is no scanning or userspace helper to run. On a system where every device is fixed and known at build time (no hot-pluggable buses, no SD card slots), skipping devtmpfs and mdev entirely can shave a measurable amount off boot time. The trade-off is that the node set is frozen: add a new peripheral and you must rebuild the image. For most modern embedded products — anything with USB, an SD slot, or removable storage — the dynamic devtmpfs-plus-mdev combination is worth the small overhead.
| Approach | Boot Cost | Handles Hotplug | Best For |
|---|---|---|---|
| Static nodes (mknod / device table) | None | No | Fixed hardware, fastest possible boot |
| devtmpfs only | Low | Partial (kernel-driven) | Simple systems, no ownership policy needed |
| devtmpfs + mdev | Low-Moderate | Yes | Most embedded products today |
Build-and-Boot Walkthrough
Add this snippet to your rootfs’s /etc/init.d/rcS and rebuild your initramfs or disk image from the earlier lectures in this series:
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mdev -s
mdev -daemon
echo "ep_rootfs: devtmpfs + mdev ready"
Boot under QEMU and check the log:
$ qemu-system-arm -M virt -kernel zImage -initrd rootfs.cpio.gz -append "console=ttyAMA0" -nographic
[ 0.812108] devtmpfs: initialized
...
ep_rootfs: devtmpfs + mdev ready
# ls -l /dev
crw-rw-rw- 1 root root 1, 3 Jan 1 00:00 null
crw-r--r-- 1 root root 1, 8 Jan 1 00:00 random
crw-r--r-- 1 root root 1, 9 Jan 1 00:00 urandom
crw------- 1 root tty 4, 64 Jan 1 00:00 ttyAMA0
Common Mistakes and Troubleshooting
- Forgetting to mount sysfs first — mdev depends entirely on
/sys; if it is not mounted,mdev -ssilently does nothing. - Wrong mdev.conf rule order — since matching stops at the first hit, an overly broad rule near the top silently shadows more specific ones below it.
- Registering both the hotplug helper and the daemon — running
echo /sbin/mdev > /proc/sys/kernel/hotplugalongsidemdev -daemoncauses duplicate handling of every event; pick one mechanism. - Missing BusyBox mdev applet — if
CONFIG_MDEVis not enabled in your BusyBox config, the binary simply is not there; check withbusybox --list | grep mdev.
Best Practices
Keep /etc/mdev.conf under version control alongside your rootfs overlay, and prefer mdev -daemon over the legacy hotplug-helper approach on any BusyBox version that supports it. For security-sensitive nodes — anything under /dev/mem, raw block devices, or debug UARTs — set explicit restrictive permissions rather than relying on the 660 default.
Summary and Key Takeaways
devtmpfs gives you a working, kernel-populated /dev with almost no configuration, and mdev layers ownership and permission policy on top of it through /etc/mdev.conf. Prefer the modern mdev -daemon netlink mode over the legacy /proc/sys/kernel/hotplug helper where available, and reserve fully static device nodes for hardware that genuinely never changes.
Conclusion
Getting device node management right is one of those root filesystem details that is invisible when it works and confusing when it does not. With devtmpfs handling kernel-driven creation and mdev applying your policy on top, you have a setup that scales from a simple QEMU test image to a production board with real hot-pluggable peripherals — a core skill in any free embedded Linux course.
Frequently Asked Questions
Do I need mdev if devtmpfs already creates device nodes?
devtmpfs creates nodes with default ownership and permissions only. mdev is what lets you customize those, react to hotplug events with scripts, and rename or relocate nodes.
What is the difference between mdev -s and mdev -daemon?
mdev -s performs a one-time scan of existing devices at boot. mdev -daemon stays running afterward, listening for new hotplug events over netlink.
Is /proc/sys/kernel/hotplug still supported?
Yes, it still works on current kernels, but it is considered legacy. The netlink-based approach used by mdev -daemon and udev is more efficient and is the preferred method today.
Can I use mdev alongside systemd-udevd?
No — the two would fight over the same uevents. Choose mdev for lightweight BusyBox-based rootfs images, or udev/systemd for larger, feature-rich distributions.
Why does my USB drive not show up in /dev?
Check that CONFIG_DEVTMPFS and the relevant USB storage drivers are enabled, that /sys is mounted, and that mdev is either running as a daemon or registered as the hotplug helper.
Where do mdev.conf rules for a new device go?
Add a new line matching the device name (as it appears under /sys/class or /sys/block) near the top of /etc/mdev.conf, before any broader catch-all rule.
Does static device node creation still have a place in modern embedded Linux?
Yes, for fixed-hardware products where boot time is critical and no hotplug ever occurs, a static device table remains a valid, simpler choice.
Continue the Free Embedded Linux Course
Next up: configuring networking on your embedded rootfs, from static IPv4 to DHCP and glibc’s name service switch.

2 Comments