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"