udev Explained

Plug in a USB drive and it instantly appears as /dev/sdb with a filesystem mounted. Plug in a printer and it's ready to use. This magic is udev — the Linux device manager. It listens for kernel hardware events and takes action: creating device files, loading drivers, mounting filesystems, and running custom scripts.

How udev Works

Hardware event sequence: 1. Hardware plugged in (USB, PCIe, etc.) 2. Kernel detects hardware → generates uevent 3. Kernel broadcasts uevent via netlink socket 4. udevd (daemon) receives uevent 5. udevd reads rules from /etc/udev/rules.d/ and /lib/udev/rules.d/ 6. Rules match device attributes (vendor, product, subsystem, etc.) 7. udevd takes action: → creates /dev/sdX device file (via mknod) → sets permissions and ownership → creates symlinks (/dev/disk/by-id/, /dev/disk/by-uuid/) → loads kernel module if needed → runs scripts or programs → notifies other daemons (udisks2, NetworkManager)
What ran before udev? Before udev (pre-2.6 kernel era), device files were created statically at install time — every possible device node existed in /dev even if the hardware wasn't present. A few thousand device files cluttered /dev. devfs tried to fix this but had fundamental design issues. udev solved the problem correctly: only devices actually present get entries, names are stable, and user space can react to hardware events.

udev Rules — How to Customize

Rules files live in /etc/udev/rules.d/ (your rules) and /lib/udev/rules.d/ (distro rules). Files processed in alphabetical order. Higher numbers win (80-*.rules overrides 60-*.rules).

# Rule syntax: KEY=="value", KEY="action" # Match keys use == (test) # Assignment keys use = or := (set) # Give USB drive a persistent name SUBSYSTEM=="block", ATTRS{idVendor}=="0781", ATTRS{idProduct}=="5571", \ SYMLINK+="my_usb_drive" # Set permissions on a device KERNEL=="video[0-9]*", GROUP="video", MODE="0660" # Run a script when USB plugged in ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="046d", \ RUN+="/usr/local/bin/camera-plugged.sh" # Rename network interface (the reason eth0 became enp3s0): SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", \ ATTRS{address}=="00:11:22:33:44:55", NAME="eth0"

Persistent Device Names

Why does /dev/sda sometimes become /dev/sdb after adding a disk? Kernel assigns names (sda, sdb) in discovery order — which can change between boots. udev solves this with symlinks in /dev/disk/by-id/ and /dev/disk/by-uuid/ that always point to the right device regardless of discovery order. Always use these stable paths in /etc/fstab and mdadm.conf.
# Stable paths created by udev automatically ls -la /dev/disk/by-id/ # ata-WDC_WD10EZEX_WD-WCC6Y0 → ../../sda # ata-WDC_WD10EZEX_WD-WCC6Y0-part1 → ../../sda1 ls -la /dev/disk/by-uuid/ # 6f6f-2b1a → ../../sda1 # abc12345-... → ../../sda2 ls -la /dev/disk/by-label/ # data → ../../sdb1 # Network interfaces — predictable names (since systemd 197): ls /sys/class/net/ # enp3s0 ← en=ethernet, p3=PCI bus 3, s0=slot 0 # wlp2s0 ← wl=wireless, p2=PCI bus 2, s0=slot 0 # lo ← loopback (always lo) # Naming scheme: # en = Ethernet # wl = Wireless LAN # ww = Wireless WAN # p<bus>s<slot> = PCI location

Monitoring Hardware Events

# Watch live udev events (plug in a device to see output) udevadm monitor # KERNEL[1234.5] add /devices/pci.../usb1/... (usb) # UDEV [1234.6] add /devices/pci.../usb1/... (usb) # Monitor only udev-processed events: udevadm monitor --udev # Get full attributes for a device (useful for writing rules): udevadm info --attribute-walk /dev/sda # looking at device '/devices/pci.../ata1/host0/.../sda': # ATTRS{model}=="WDC WD10EZEX" # ATTRS{vendor}=="ATA " # ATTRS{rev}=="1A01" # For USB device: udevadm info --attribute-walk /dev/bus/usb/001/002 # Test if a rule matches a device (without triggering action): udevadm test /sys/block/sda # Reload rules after editing (no reboot needed): udevadm control --reload-rules udevadm trigger # re-run rules on existing devices

Practical Rule Examples

# /etc/udev/rules.d/99-my-devices.rules # Mount USB drive automatically when plugged in ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_TYPE}!="", \ RUN+="/usr/bin/systemd-mount --no-block %N" # Give Arduino consistent device name SUBSYSTEM=="tty", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0043", \ SYMLINK+="arduino", MODE="0666" # Disable USB autosuspend for a specific device ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="046d", \ ATTRS{idProduct}=="c52b", ATTR{power/autosuspend}="-1" # Lock screen when a YubiKey is removed ACTION=="remove", SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", \ RUN+="/usr/bin/loginctl lock-sessions" # Create /dev/video_webcam symlink KERNEL=="video[0-9]*", ATTRS{idVendor}=="046d", \ SYMLINK+="video_webcam"