udev Rules: Automatically Handling Device Events
AI generated
$
/etc
Linux
udev Rules
Automatically handling device events

As soon as a server gains multiple network interfaces, additional storage devices or USB hardware, the kernel's default automatic naming often stops being good enough: after a reboot, the disk that was sdb yesterday suddenly becomes sdc, and any script relying on that name silently breaks. udev solves exactly this problem by intercepting kernel device events and reacting to them with custom rules, from persistent names to automatically triggered actions.

10 min read Linux udev Hardware

1. How udev processes kernel device events

Whenever the kernel detects a device, whether at boot or when a USB drive is plugged in later, it sends a so called uevent over the netlink socket to userspace. udev, an integral part of systemd for several years now, receives that event, enriches it with additional information from sysfs, and decides how to react based on its rules.

This separation between kernel and userspace is deliberate: the kernel itself has no concept of arbitrary device naming, permissions for specific user groups, or automatically starting a script. udev handles all of that in userspace, which keeps configuration flexible and changeable without a kernel restart, while the kernel itself stays lean and generic.


# Watch uevents in real time while a device is plugged in or removed
udevadm monitor --udev --property

2. Rule syntax: match keys and assign keys

Every udev rule consists of a combination of match keys, written with a double equals sign ==, which determine whether a rule applies at all, and assign keys with a single equals sign =, which set an action or property. Typical match keys are KERNEL for the kernel assigned device name, SUBSYSTEM for the device class such as block or net, and ATTR{...} for attributes from sysfs.

Assign keys such as SYMLINK+=, OWNER=, MODE= or RUN+= determine what happens to the device once all match conditions of a rule are satisfied. The plus sign on SYMLINK+= and RUN+= matters: it appends to existing values instead of overwriting them, so multiple rules can independently contribute additional symlinks or actions.

3. Creating custom rule files in /etc/udev/rules.d

Distributions ship default rules in /usr/lib/udev/rules.d/, which can be overwritten by a package update. Custom rules therefore belong exclusively in /etc/udev/rules.d/, which takes precedence over the default rules and stays untouched by package updates. The file name follows a convention of a two digit priority number followed by a descriptive name, for example 70-persistent-storage.rules.

The priority determines the processing order: lower numbers are evaluated first. Rules in the 60 to 69 range are traditionally reserved for persistent device naming, while rules starting at 90 typically trigger actions such as starting scripts or services after basic properties have already been set.


# Create a custom rule file, numbered for processing order
sudo nano /etc/udev/rules.d/70-persistent-storage.rules

# After changes: reload rules without restarting the server
sudo udevadm control --reload-rules
sudo udevadm trigger

4. Practical example: persistent device names for storage

Classic kernel names like /dev/sdb are not guaranteed to stay stable, because the order in which devices are detected at boot can vary, for example when an external disk is detected slightly later than expected. For storage in production servers, especially with RAID or LVM setups, a stable, hardware bound name is therefore essential, one that does not shift with boot order.

A custom rule can read the disk's serial number from sysfs and turn it into an additional, guaranteed stable symlink. In addition, udev already ships default symlinks under /dev/disk/by-id/, /dev/disk/by-uuid/ and /dev/disk/by-path/, which are sufficient for most cases and should be used in /etc/fstab instead of the raw /dev/sdX name.


# /etc/udev/rules.d/70-persistent-storage.rules
# Create a custom, stable symlink based on the serial number
SUBSYSTEM=="block", KERNEL=="sd?", ENV{ID_SERIAL}=="WD-WCC4N1234567", \
  SYMLINK+="disk/backup-volume"

# List symlinks already generated automatically by udev
ls -la /dev/disk/by-id/

5. Testing and debugging rules with udevadm

Before a new rule goes live, udevadm info shows every available property of an already present device that could serve as a match key in a rule. This is the first step with any new rule, because it is far more reliable to read actually existing attributes than to guess them.

For simulating the actual rule run, udevadm test replays rule processing for a specific device and logs in detail which rules would apply and which actions would fire, without actually changing the system. Only after a successful test run should udevadm trigger actually apply the rules to running devices.


# Show all properties of a device usable as match keys
udevadm info --query=all --name=/dev/sdb

# Simulate rule processing without changing the system
udevadm test /sys/class/block/sdb

# Actually apply rules to already present devices
udevadm trigger --subsystem-match=block

6. RUN+= actions and common timing pitfalls

The assign key RUN+= launches an external program once a rule matches. A common beginner mistake is trying to start a long running process directly through it: udev rules must be processed extremely fast, and a blocking call inside RUN+= delays processing of all subsequent uevents, in the worst case system wide.

The correct approach for longer actions is to launch a standalone systemd transient service via RUN+="/usr/bin/systemd-run ...", running outside the udev processing pipeline. That keeps the actual udev rule short and fast, while the real work runs asynchronously in a separate process.


# Wrong: blocks udev processing
# RUN+="/usr/local/bin/backup-new-disk.sh"

# Right: launches an independent, non blocking service
RUN+="/usr/bin/systemd-run --no-block /usr/local/bin/backup-new-disk.sh"

7. How udev and systemd device units work together

systemd automatically creates a so called device unit for every device recognized by udev, provided it is marked with the sysfs property SYSTEMD_WANTS or a matching rule. Other systemd units can then reference this device unit in their Requires= or After= directive and only start once the corresponding device is actually available.

This pattern is especially relevant for storage: a mount unit for an external backup volume can be configured with Requires=dev-disk-by\x2did-... so that systemd automatically waits until the device has been recognized and named by udev, instead of failing at boot because the device is not yet ready.

8. Practical example: setting persistent names for network interfaces

Servers with multiple network interfaces suffer from the same underlying problem as storage devices: the kernel assigned name such as eth0 or eth1 depends on detection order at boot and can suddenly change after a kernel update or when an additional card is added. Modern distributions therefore default to predictable names such as enp3s0, derived from the card's physical PCI position, which stay stable as long as the hardware is not physically moved.

For cases where even these predictable names are not enough, for example when two identical cards need to be clearly named by their role on the network, a custom udev rule allows an even more targeted mapping based on the MAC address. That way the interface for the internal storage network can reliably be named storage0, regardless of whatever name the kernel itself would have assigned.


# /etc/udev/rules.d/71-net-storage-interface.rules
# Uniquely name a network interface based on its MAC address
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="00:1a:2b:3c:4d:5e", \
  NAME="storage0"

# Check the current mapping of names to MAC addresses
ip -o link show

9. Best practices and debugging in a running system

Every custom rule should start with match keys as specific as possible, to avoid unexpected matches against other devices. An overly broad match such as just SUBSYSTEM=="block" without further restriction potentially matches every disk in the system and can unintentionally override existing rules or create conflicts.

For live debugging, journalctl -u systemd-udevd provides the udev daemon's own log output, while udevadm monitor shows in real time which uevents are actually arriving. This combination makes it visible whether a problem actually lies in a custom rule, or whether the expected uevent is simply never fired by the kernel in the first place.

Key Type Meaning Example
KERNEL== Match Kernel assigned device name KERNEL=="sd?"
SUBSYSTEM== Match Device class such as block or net SUBSYSTEM=="net"
ATTR{name}== Match Attribute value from sysfs ATTR{idVendor}=="046d"
SYMLINK+= Assign Create an additional device path SYMLINK+="disk/backup"
RUN+= Assign Launch an external program or service RUN+="/usr/bin/systemd-run ..."

Mironsoft

Server administration, Docker hosts, and performance tuning

Linux servers nobody on the team really understands anymore?

We handle setup, hardening, and performance tuning of Linux servers and Docker hosts for Magento deployments, documented and traceable instead of grown and unclear.

Server Audit

Review the existing server configuration for security gaps and performance bottlenecks.

Docker Host Setup

Set up and secure production-ready Docker environments for Magento cleanly.

Monitoring & Tuning

Measure resource usage and tune systemd, kernel, and services with purpose.

10. Summary

udev Rules

Rule directory

/etc/udev/rules.d, takes precedence over defaults

Core command

udevadm test for simulation without system changes

Most common use case

Persistent storage names based on serial number

Biggest pitfall

Blocking RUN+= calls instead of systemd-run

11. FAQ: udev Rules

1Why should I not place custom rules in /usr/lib/udev/rules.d?
That directory is managed by the distribution and gets overwritten on a package update. Custom rules belong in /etc/udev/rules.d, which takes precedence and stays untouched by updates.
2How do I find out which attributes a device exposes for a rule?
The command udevadm info --query=all --name=/dev/sdX lists all available properties and attributes of the device, which can then be used as match keys in a custom rule.
3Why does the device name sdb sometimes change after a reboot?
Kernel naming depends on the order devices are detected at boot, which can vary with multiple storage devices. Persistent symlinks via udev rules solve this problem reliably.
4What is the difference between udevadm test and udevadm trigger?
udevadm test simulates rule processing for a device and logs the result without changing the system. udevadm trigger actually applies rules to running devices and fires real actions.
5Can I launch a long running backup script directly in RUN+=?
Not recommended, since udev rules must be processed extremely fast. A blocking call delays processing of subsequent uevents, which is why systemd-run with the --no-block option is the correct solution.
6How do I reload changed udev rules without a reboot?
udevadm control --reload-rules reloads the rule files, and a following udevadm trigger applies the updated rules to already present devices.
7What does the plus sign in SYMLINK+= do compared to SYMLINK=?
The plus sign appends to existing symlinks instead of replacing them. That lets multiple independent rules each contribute their own additional symlinks for the same device without overwriting each other.
8Why does udev already provide symlinks under /dev/disk/by-id by default?
Those symlinks are automatically generated by generic, shipped rules from hardware attributes such as the serial number, and already cover the most common persistent naming use case without needing custom rules.
9How do I watch uevents in real time while debugging?
udevadm monitor --udev --property shows every incoming uevent live, including all associated properties, which is especially helpful to check whether an event reaches the system at all.
10Where do I find error messages from the udev daemon itself?
journalctl -u systemd-udevd shows the log output of the running udev daemon, including syntax errors in custom rule files and warnings about failed actions.