How to Configure the Ubuntu udev Daemon to Trigger Scripts on USB Insertion

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/sdb and 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.

RELATED POSTS

  • How to Install and Configure the Proxmox Backup Server (PBS) Client on Debian
  • How to Install and Configure the Postfix Mail Server on Ubuntu
  • How to Join an Ubuntu Linux Machine to a Windows Active Directory Domain using SSSD
  • How to Use the fuser Command to Identify Processes Locking a File in Linux
  • How to Install and Configure the Memcached Object Caching System on Ubuntu
  • Get the best tech tips delivered straight to your inbox.

    Join thousands of readers mastering Apple, Google, Microsoft, and Linux.