Skip to content
LinuxDeep Dive July 11, 2026 5 min readViews unavailable

How udev and the Device Model Actually Discover and Name Hardware

The path from a kernel detecting new hardware to a predictably-named device node appearing in /dev, and why udev's naming rules exist at all instead of just using whatever the kernel calls a device.

Plugging in a USB drive on Linux triggers a chain of events spanning the kernel, sysfs, and userspace’s udev daemon before a usable, predictably-named device node shows up under /dev — and the reason this chain exists, rather than the kernel just creating /dev entries directly with fixed names, comes down to a genuine problem: kernel-assigned device names are fundamentally unstable across boots and hardware changes, and modern systems need something better than that instability.

The kernel’s role: detection and sysfs, not naming policy

When the kernel detects new hardware — a USB device enumerated, a new block device appearing — it creates a representation of that device in sysfs (/sys), a virtual filesystem exposing kernel device-model objects as a browsable directory tree, and emits a uevent, a kernel-to-userspace notification that new hardware has appeared, disappeared, or changed state. Critically, the kernel deliberately does not decide what that device should be called in /dev — historically it would assign a name based on detection order (sda, sdb, and so on for SCSI/SATA disks), but detection order is not a stable property across reboots, especially with multiple similar devices, hot-plugged hardware, or asynchronous device enumeration where the order two similar drives are detected in can genuinely vary from boot to boot.

udev: turning uevents into policy-driven device nodes

udev, running as a userspace daemon, listens for these kernel uevents and is responsible for the actual policy of what device nodes get created, what permissions they have, and what additional predictable, stable symlinks point at them. This is a deliberate userspace/kernel split: the kernel’s job is detecting hardware and exposing raw information about it; udev’s job is turning that raw information into a coherent, administrator-configurable naming and permission scheme, which is a policy decision that’s much easier to inspect, override, and reason about living in userspace configuration files than baked into kernel code.

Rules files: how naming policy is actually expressed

udev’s behavior is governed by rules files, matched against uevent properties for each device as it appears:

# /etc/udev/rules.d/99-my-device.rules
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="myserial"

This rule matches any TTY device with a specific USB vendor/product ID pair (a common FTDI USB-serial chip, in this example) and creates a stable symlink /dev/myserial pointing at whatever kernel-assigned device name (ttyUSB0, ttyUSB1, whichever the kernel happened to assign this time) that specific device actually got. This is the mechanism that solves the unstable-naming problem directly: instead of a script or configuration hardcoding /dev/ttyUSB0 and breaking whenever plug order changes, it references /dev/myserial, which udev guarantees points at the correct physical device regardless of what kernel-level name that device happened to be assigned this particular boot.

Persistent naming for storage: the pattern most people actually encounter

The most commonly encountered instance of this problem-and-solution pair is disk naming. /dev/sda and /dev/sdb are kernel-assigned, detection-order-dependent names — genuinely unstable across reboots on systems with multiple similar drives, since which physical drive gets enumerated first isn’t guaranteed to be consistent. udev addresses this by populating /dev/disk/by-id/, /dev/disk/by-uuid/, and /dev/disk/by-path/ with symlinks based on stable device properties (a drive’s actual serial number, its filesystem UUID, or its physical connection topology) rather than detection order:

ls -la /dev/disk/by-uuid/

This is exactly why /etc/fstab entries generated by modern installers reference a filesystem’s UUID (UUID=xxxx-xxxx /home ext4 defaults 0 2) rather than a raw device path like /dev/sda1 — the UUID-based reference survives drive reordering, additional drives being added, or even the drive being moved to a different SATA/USB port entirely, while a raw /dev/sda1 reference could silently start pointing at a completely different physical drive after any of those changes.

Predicates and matching: how rules avoid over-matching

udev rules match against a rich set of device attributes exposed via sysfs — vendor/product IDs, kernel subsystem, driver name, physical bus path, and more — which is what lets rules be as narrow or broad as the situation actually requires. A rule matching only on SUBSYSTEM=="tty" alone would apply to every serial-like device on the system, almost certainly not what’s intended; adding the specific vendor/product ID attributes narrows it to exactly the physical hardware the rule author actually cares about, which is the difference between a rule that reliably does one specific thing and one that unpredictably fires for unrelated hardware sharing the same broad subsystem.

Debugging udev rules that aren’t matching as expected

udevadm test and udevadm monitor are the practical tools for understanding why a rule isn’t behaving as expected, rather than editing rules and hoping:

udevadm monitor --udev
udevadm test /sys/class/tty/ttyUSB0

udevadm monitor shows uevents as they happen live, which is the fastest way to confirm a device is actually generating the uevent you expect when plugged in. udevadm test runs the rule-matching logic against a specific already-existing sysfs device path and reports which rules matched and what actions they would take, without actually re-triggering the device — invaluable for iterating on a rule’s exact match conditions without needing to physically unplug and replug hardware after every edit.

The general principle this all reflects

The kernel/udev split — kernel detects and exposes raw facts, userspace decides naming and permission policy based on those facts — is a specific instance of a broader Linux design pattern: keep the kernel’s job narrow and mechanical, push policy decisions that benefit from being inspectable, configurable, and correctable without a kernel rebuild out into userspace wherever the split can be made cleanly. Device naming is one of the clearest, most consequential examples of that pattern actually solving a real, previously painful problem.