USB Passthrough on KVM and Proxmox: Connecting Security Dongles, Cameras, and 3G Modems to Virtual Machines

Virtualization tutorial - IT technology blog
Virtualization tutorial - IT technology blog

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)

  1. Go to Proxmox Web UI → select the VM → Hardware tab
  2. Click Add → select USB Device
  3. Choose Use USB Vendor/Device ID → the dropdown lists all USB devices currently plugged into the host
  4. Select the device to pass through → OK
  5. 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.

Share: