In the past, I used a mechanism based on scripts called by udev rules [1,2] to automatically add USB drives to VMs. On a new machine this fails now with the following message:

libvirtd[832]: internal error: unable to execute QEMU command 'device_add': failed to find host usb device 3:3

It looks like the device is not ready yet when the script is called. Even adding a small delay using sleep does not help. Probably, the device does not settle until all udev rules have finished.

In order to disconnect the attach script from the udev rules, I found a way to use systemd services to automatically attach and detach the USB drive. For some devices, systemd automatically creates a .device device unit configuration. This service starts and stops automatically when a device appears and vanishes.  Hence, we can bind another service to this service that will execute our script. Unfortunately, for my USB drive, it creates a service file with the following name sys-devices-pci0000:00-0000:00:10.0-usb3-3\x2d1.device. This name changes every time we plug in the device. To make the naming reliable, I created a udev rule with a SYMLINK setting similar to this

SUBSYSTEM=="usb", \
    ENV{ID_VENDOR_ID}=="<usb vendor id>", \
    ENV{ID_MODEL_ID}=="<usb model id>", \
    SYMLINK+="<my USB drive name>", \
    TAG+="systemd", \

Afterwards, udev creates a device /dev/myChosenName and the last two lines cause systemd to create a dev-myChosenName.device unit and to start the my-usb-hotplug.service unit when the drive is plugged in. The my-usb-hotplug.service unit looks like this:

Description=my usb hotplug service

ExecStart=/usr/local/sbin/ add <my vm name> <usb vendor id> <usb model id>
ExecStop=/usr/local/sbin/ remove <my vm name> <usb vendor id> <usb model id>


With the BindsTo= and After= settings, we make sure that this service is also stopped when the device service is stopped. The file looks like this:

#! /bin/bash


if [ "${ACTION}" == "add" ]; then

virsh ${CMD} ${VM_NAME} /dev/stdin <<EOF
<hostdev mode='subsystem' type='usb'>
   <vendor id="0x${ID_VENDOR_ID}" />
   <product id="0x${ID_MODEL_ID}" />

After all files are in place:

  • make the script executable: chmod +x /usr/local/sbin/
  • reload the udev rules with sudo udevadm control -R
  • reload systemd service units with sudo systemctl daemon-reload
  • enable the script service unit: sudo systemctl enable my-usb-hotplug.service