The Problem: Physical USB Devices Not Visible Inside a VM
I had a security dongle (USB hardware key) required to activate accounting software. The catch: the software only runs on Windows, while my host runs Ubuntu with KVM. Plugging the dongle into the host works fine — but the Windows VM sees nothing.
The same issue applies to 3G/4G USB modems — you might want to assign one permanently to a router VM so it handles dial-up independently of the host. Or a USB camera you want to pass directly into a VM running NVR software.
All of these are solved by USB Passthrough: the virtual machine takes full control of the physical USB device and communicates directly with its firmware — no intermediary layer.
Why VMs Can’t See Physical USB by Default
KVM/QEMU creates a virtual (emulated) USB controller by default. The VM sees a clean, simulated controller with no physical devices attached. The hypervisor sits between host and VM — and by default, it does not forward USB hardware down to the guest.
To make passthrough work, QEMU uses the usb-host mechanism: it maps the device file /dev/bus/usb/... directly from the host into the VM via VFIO or libusb. The VM then receives full USB descriptors, endpoints, and communicates directly with the device firmware — exactly as if the device were physically plugged in. If you want to go deeper on how QEMU tuning affects guest device performance, that’s a rabbit hole worth exploring.
Proxmox wraps this with a Web UI for convenience, but underneath it’s the same QEMU mechanism.
Two Passthrough Methods — Which Should You Use?
Method 1: Passthrough by Vendor ID and Product ID (Recommended)
Assigns the device by model — Vendor:Product ID. The VM recognizes the device regardless of which physical port it’s plugged into. This is the right approach for security dongles, 3G modems, or any device you need to assign permanently.
Method 2: Passthrough by Bus:Device Address
Assigns by physical location — which bus and which port. Unplugging and replugging the device changes its address, and the VM immediately loses the connection. Only use this when you need to control a specific port rather than a specific device.
Setup on KVM/libvirt
Step 1: Find the Vendor ID and Product ID
Plug the device into the host and run:
lsusb
Sample output:
Bus 001 Device 003: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 004: ID 12d1:1506 Huawei Technologies Co., Ltd. Modem/Networkcard
Bus 002 Device 002: ID 0529:0001 Aladdin Knowledge Systems HASP v0.06
The last line is the HASP dongle — 0529 is the Vendor ID, 0001 is the Product ID. Note these two values down.
Step 2: Add the USB Device to the VM’s XML
Using virsh edit is the quickest approach — if you’re not yet familiar with managing KVM VMs via libvirt and virsh, it’s worth a read before diving in:
virsh edit vm-name
Find the <devices> section and add the following inside it:
<hostdev mode='subsystem' type='usb' managed='yes'>
<source>
<vendor id='0x0529'/>
<product id='0x0001'/>
</source>
</hostdev>
Save and restart the VM:
virsh shutdown vm-name
virsh start vm-name
Step 3: Verify Inside the VM
On a Windows VM running on KVM/Proxmox, open Device Manager — the device should appear immediately. On a Linux VM, check with:
lsusb # run inside the VM
Hot-plug: Adding USB While the VM Is Running
No restart needed — create a temporary XML file and attach it directly:
cat > /tmp/usb-dongle.xml <<EOF
<hostdev mode='subsystem' type='usb' managed='yes'>
<source>
<vendor id='0x0529'/>
<product id='0x0001'/>
</source>
</hostdev>
EOF
virsh attach-device vm-name /tmp/usb-dongle.xml --live
To detach while the VM is still running:
virsh detach-device vm-name /tmp/usb-dongle.xml --live
Setup on Proxmox VE
My homelab runs Proxmox managing 12 VMs and containers — it’s where I test everything before pushing to production. USB passthrough is a feature I use nearly every week, especially when testing software that requires a license dongle. If you’re still setting up your Proxmox environment, the guide on creating and managing VMs with Proxmox VE covers the essentials.
Via Web UI (Fastest)
- Go to Proxmox Web UI → select the VM → Hardware tab
- Click Add → select USB Device
- Choose Use USB Vendor/Device ID → the dropdown lists all USB devices currently plugged into the host
- Select the device to pass through → OK
- Start (or restart) the VM
Proxmox automatically writes the config to /etc/pve/qemu-server/<VMID>.conf:
usb0: host=0529:0001
Via Config File (For Automation or Scripting)
SSH into the Proxmox node and edit the config file directly:
nano /etc/pve/qemu-server/100.conf # 100 is the VMID
Add:
# Accounting software dongle
usb0: host=0529:0001
# Second Huawei modem
usb1: host=12d1:1506
Restart the VM:
qm stop 100 && qm start 100
Passing Through an Entire Physical USB Port
To permanently assign a specific USB port (regardless of what device is plugged in), use the Use USB Port option in Proxmox UI. The corresponding config entry looks like:
usb0: host=1-2 # Bus 1, Port 2
Identify the bus/port with:
lsusb -t # View USB topology tree
Troubleshooting Common Issues
Permission Denied When Attaching USB
Libvirt needs read access to the device file. Check the ownership first:
ls -la /dev/bus/usb/001/004 # replace with your actual bus/device numbers
Is the owner root:root while QEMU runs as a different user? Add a udev rule:
cat > /etc/udev/rules.d/99-usb-passthrough.rules <<EOF
SUBSYSTEM=="usb", ATTR{idVendor}=="0529", ATTR{idProduct}=="0001", MODE="0666"
EOF
udevadm control --reload-rules
udevadm trigger
Device Disconnects After VM Restart
Almost certainly you’re using Bus:Device passthrough instead of Vendor:Product. The Bus:Device address changes every time the device is re-enumerated. Switch to Vendor:Product ID and the problem goes away.
USB 3.0 Device Not Recognized in VM
QEMU defaults to a USB 2.0 controller (EHCI). USB 3.0 devices require xHCI. In Proxmox UI: Hardware → Add → USB Controller → select xHCI. Or in the libvirt XML:
<controller type='usb' model='qemu-xhci'/>
Script to Auto-Attach USB When Plugged In
A small homelab script that attaches a USB device to a VM the moment it’s plugged in, without any manual intervention:
#!/bin/bash
# usb-attach.sh — Attach a USB device to a VM when plugged in
# Use with a udev rule or run manually
VENDOR_ID="0529"
PRODUCT_ID="0001"
VM_NAME="windows-accounting"
if virsh list --state-running | grep -q "$VM_NAME"; then
echo "VM is running, attaching USB..."
virsh attach-device "$VM_NAME" - --live <<EOF
<hostdev mode='subsystem' type='usb' managed='yes'>
<source>
<vendor id='0x${VENDOR_ID}'/>
<product id='0x${PRODUCT_ID}'/>
</source>
</hostdev>
EOF
echo "Attach successful!"
else
echo "VM is not running, skipping."
fi
Summary
The entire process comes down to two steps: find the Vendor:Product ID of the device, declare it in the VM config. Done. Proxmox handles this in a few clicks through the Web UI. KVM/libvirt gives you finer control via virsh edit and virsh attach-device — including hot-plug while the VM is running.
One important takeaway: always use Vendor:Product ID, never Bus:Device. I once spent an entire afternoon debugging a dongle that “disappeared” after every restart — it turned out the Bus:Device address was changing each time. Switching to Vendor:Product made it permanent, regardless of which port the dongle is plugged into.

