The Challenge of Hardware Events in Linux
When you plug a USB drive, a network adapter, or a new keyboard into an Ubuntu machine, the Linux kernel instantly detects the hardware and loads the appropriate driver. In a desktop environment, a graphical daemon typically takes over, automatically mounting the USB drive and opening a file manager window.
However, on a headless Ubuntu Server running in a data center or a customized embedded Linux device, there is no graphical environment. If you want the server to automatically mount a USB backup drive, execute an rsync script to copy database dumps onto it, and then cleanly unmount it the second the drive is inserted, you must interface directly with the kernel’s device manager.
This is accomplished using the udev (userspace /dev) daemon. Udev listens for hardware events sent by the kernel (uevents) and executes predefined rules, allowing administrators to trigger bash scripts instantly based on hardware insertion.
Step 1: Identifying the Device Attributes
Before writing a rule, you must identify the unique hardware attributes (such as the Vendor ID or Serial Number) of the specific USB drive you want to trigger the script. If you don’t filter by a specific ID, your backup script will run every time any USB device is plugged in.
First, plug in the target USB drive and identify its block device name (e.g., /dev/sdb1) using the lsblk command.
Next, use the udevadm command to dump all the hardware attributes for that specific device:
udevadm info --name=/dev/sdb1 --attribute-walk
The output will trace the device up the hardware tree. Look for the block representing the USB storage device and find the idVendor and idProduct strings. For example:
ATTRS{idVendor}=="0781"
ATTRS{idProduct}=="5581"
Step 2: Writing the Bash Script
Write the script that you want udev to execute. We will create a simple script that mounts the drive, copies a file, and unmounts it.
sudo nano /usr/local/bin/usb_backup.sh
Add the following bash logic. (Note: udev executes scripts as the root user, but it provides a very limited environment path, so you must use absolute paths for commands).
#!/bin/bash
# Wait briefly for the kernel to finish initializing the block device
/bin/sleep 2
# Mount the device (passing the device node via an argument)
/bin/mount $1 /mnt/usb_backup
# Execute the backup
/usr/bin/rsync -a /var/lib/mysql/ /mnt/usb_backup/mysql_backup/
# Unmount the device
/bin/umount /mnt/usb_backup
Make the script executable:
sudo chmod +x /usr/local/bin/usb_backup.sh
Step 3: Creating the udev Rule
Udev rules are stored in the /etc/udev/rules.d/ directory. They are parsed in alphabetical order, so custom rules usually start with a high number (like 99) to ensure they override system defaults.
Create a new rule file:
sudo nano /etc/udev/rules.d/99-usb-backup.rules
A udev rule consists of matching conditions (keys) followed by an action.
ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem", ATTRS{idVendor}=="0781", ATTRS{idProduct}=="5581", RUN+="/usr/bin/systemd-run /usr/local/bin/usb_backup.sh %k"
Decoding the Rule:
ACTION=="add": Only trigger when a device is inserted, not removed.SUBSYSTEM=="block": Only trigger for block storage devices, not raw USB buses.ENV{ID_FS_USAGE}=="filesystem": Only trigger when the partition containing the filesystem is recognized (ignoring the raw disk/dev/sdband acting on the partition/dev/sdb1).ATTRS{...}: Matches the specific vendor of your backup drive.RUN+=...: The action to execute.
Step 4: The systemd-run Workaround
In modern Ubuntu systems utilizing systemd, udev strictly enforces a timeout on the RUN directive. If your backup script takes longer than 5 seconds to complete, systemd will ruthlessly kill the process, assuming the device has hung.
Furthermore, udev blocks the mounting of filesystems if they are tied to a process that is currently blocking the udev event loop.
To bypass this, you must not run the script directly. Instead, you use /usr/bin/systemd-run to spawn an independent, detached background service that executes your script. The %k variable passes the kernel name (e.g., /dev/sdb1) to the script as the $1 argument.
Step 5: Reloading and Testing
Once the rule is saved, you must instruct the udev daemon to reload its rules database.
sudo udevadm control --reload-rules
Unplug the USB drive and plug it back in. To verify that the rule triggered and the systemd unit executed the script, you can check the syslog:
grep -i systemd /var/log/syslog | tail
You should see a log entry stating Started /usr/local/bin/usb_backup.sh sdb1, confirming that the backup is executing silently in the background.
Conclusion
The udev daemon provides Linux administrators with absolute control over the kernel’s hardware events. By combining highly specific device attribute matching with detached systemd-run execution, organizations can build entirely headless, automated data ingestion and extraction workflows triggered by the simple insertion of a physical USB device.