In this post, I configure Android phone USB passthrough for a KVM virtual machine on an Ubuntu Server host.
The goal is:
Physical Android phone
→ Ubuntu KVM host
→ KVM VM
→ adb devices inside the VMThis is useful when the Android phone is physically connected to the KVM host, but I want to use it inside a VM for Android or Flutter development.
This post does not cover installing KVM from scratch. If you have not installed KVM and Cockpit yet, refer to my previous post:
Environment
In this example:
KVM host: Ubuntu Server
VM name: client
Phone: Samsung Galaxy phone
Phone USB ID: 04e8:6860Inside the VM:
VM user: administrator
Use case: adb / Flutter developmentThe Android phone is physically plugged into the KVM host, not directly into the VM.
- Check the USB Device on the KVM Host
Run this on the KVM host:
lsusbIn my case, I saw two Samsung devices:
Bus 002 Device 004: ID 04e8:4001 Samsung Electronics Co., Ltd PSSD T7
Bus 007 Device 005: ID 04e8:6860 Samsung Electronics Co., Ltd Galaxy series, misc. (MTP mode)The Samsung T7 external SSD is:
04e8:4001Do not pass this device through to the VM unless you really mean to.
The Android phone is:
04e8:6860So the values I need are:
Vendor ID: 04e8
Product ID: 6860- Create a libvirt USB Passthrough XML File
Run this on the KVM host:
sudo mkdir -p /etc/libvirt/usb
sudo nano /etc/libvirt/usb/samsung-phone.xmlAdd this XML:
<hostdev mode='subsystem' type='usb'>
<source startupPolicy='optional'>
<vendor id='0x04e8'/>
<product id='0x6860'/>
</source>
</hostdev>The important part is:
<vendor id='0x04e8'/>
<product id='0x6860'/>I also use:
startupPolicy='optional'This allows the VM to start even when the phone is not currently plugged in.
- Attach the USB Device to the VM
If the VM is already running, run this on the KVM host:
sudo virsh attach-device client /etc/libvirt/usb/samsung-phone.xml --live --configThe options mean:
--live Apply to the currently running VM
--config Save to the persistent VM configurationIf the VM is stopped, run:
sudo virsh attach-device client /etc/libvirt/usb/samsung-phone.xml --config
sudo virsh start clientConfirm the VM configuration:
sudo virsh dumpxml client | grep -A8 -B2 "04e8"Expected output:
<hostdev mode='subsystem' type='usb'>
<source startupPolicy='optional'>
<vendor id='0x04e8'/>
<product id='0x6860'/>
</source>
</hostdev>- Check the Device Inside the VM
SSH into the VM and run:
lsusb | grep SamsungExpected output:
Bus 001 Device 006: ID 04e8:6860 Samsung Electronics Co., Ltd Galaxy series, misc. (MTP mode)At this point, USB passthrough is working.
However, ADB may still fail because Linux USB permissions inside the VM are separate from KVM USB passthrough.
- Install Android USB Permission Rules Inside the VM
Inside the VM, install the Android udev rules package:
sudo apt update
sudo apt install -y android-sdk-platform-tools-commonThis package is separate from the Android SDK you may install manually. Its purpose here is to provide Linux udev rules for Android USB devices.
Make sure the VM user is in the plugdev group:
sudo usermod -aG plugdev administratorLog out and log back in:
exitAfter logging back in, confirm:
idThe output should include:
plugdevNow test ADB:
adb kill-server
adb start-server
adb devicesIf everything is correct, the result should eventually become:
List of devices attached
R5CR80PJYZF deviceIf ADB shows:
R5CR80PJYZF unauthorizedunlock the phone and accept the USB debugging authorization prompt.
- Manually Detach on Unplug and Reattach on Replug
When the phone is passed through to the VM, unplugging the physical phone from the KVM host may not immediately remove the USB device from the guest VM.
In other words, after unplugging the phone, the VM may still show the Samsung USB device:
lsusb | grep SamsungThis does not always mean the phone is still usable. It may simply be a stale USB device state inside the guest.
When the phone is physically unplugged, I can manually detach it from the running VM on the KVM host:
sudo virsh detach-device client /etc/libvirt/usb/samsung-phone.xml --liveAfter that, the VM should no longer show the phone:
lsusb | grep SamsungWhen I plug the phone back into the KVM host, first confirm that the host sees it again:
lsusb | grep -i SamsungExpected output:
ID 04e8:6860 Samsung Electronics Co., Ltd Galaxy seriesThen attach it back to the running VM:
sudo virsh attach-device client /etc/libvirt/usb/samsung-phone.xml --liveNow check inside the VM again:
lsusb | grep Samsung
adb devicesExpected ADB result:
List of devices attached
R5CR80PJYZF deviceThis manual detach/attach flow confirms the correct behavior:
Physical unplug → VM should stop seeing the phone
Physical replug → VM should see the phone againThe optional auto-sync section later automates this same behavior with a host-side udev rule and systemd service.
- Optionally Automatically Sync USB State After Unplug and Replug
The previous step showed the manual way to keep the VM USB state in sync with the physical phone state.
When the phone is unplugged, the VM should detach the USB device.
When the phone is plugged back in, the VM should attach it again.
To automate that behavior, I use a host-side udev rule to trigger a systemd service.
This step must be configured on the KVM host, not inside the VM.
- Create the Host-Side Script
Run this on the KVM host:
sudo vi /usr/local/sbin/samsung-phone-client-usb.shAdd:
#!/usr/bin/env bash
set -u
ACTION="${1:-add}"
VM="client"
XML="/etc/libvirt/usb/samsung-phone.xml"
VIRSH="/usr/bin/virsh -c qemu:///system"
if ! $VIRSH domstate "$VM" 2>/dev/null | grep -q "running"; then
exit 0
fi
wait_for_phone() {
for i in {1..15}; do
if /usr/bin/lsusb -d 04e8:6860 >/dev/null 2>&1; then
return 0
fi
sleep 1
done
return 1
}
case "$ACTION" in
add)
# Wait until the phone is fully visible on the host.
if ! wait_for_phone; then
exit 0
fi
# Clear stale live state if it exists.
$VIRSH detach-device "$VM" "$XML" --live >/dev/null 2>&1 || true
# Retry attach because udev can fire before libvirt can use the device.
for i in {1..10}; do
if $VIRSH attach-device "$VM" "$XML" --live >/dev/null 2>&1; then
exit 0
fi
sleep 1
done
exit 1
;;
remove)
# The physical device may already be gone. Ignore detach errors.
$VIRSH detach-device "$VM" "$XML" --live >/dev/null 2>&1 || true
;;
*)
exit 1
;;
esacMake it executable:
sudo chmod +x /usr/local/sbin/samsung-phone-client-usb.sh- Create the systemd Service
Run this on the KVM host:
sudo vi /etc/systemd/system/samsung-phone-client-usb@.serviceAdd:
[Unit]
Description=Handle Samsung phone USB %i for client VM
After=libvirtd.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/samsung-phone-client-usb.sh %iReload systemd:
sudo systemctl daemon-reload- Create the Host udev Rule
Run this on the KVM host:
sudo vi /etc/udev/rules.d/90-samsung-phone-kvm.rulesAdd:
ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="04e8", ATTR{idProduct}=="6860", RUN+="/usr/bin/systemctl --no-block start samsung-phone-client-usb@add.service"
ACTION=="remove", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="4e8/6860/*", RUN+="/usr/bin/systemctl --no-block start samsung-phone-client-usb@remove.service"This rule is different from Android ADB udev rules.
This host-side rule does this:
USB add/remove event
→ systemd service
→ virsh attach-device / detach-device
→ phone is attached to or detached from the VMThe Android ADB rule inside the VM does this:
USB device inside VM
→ normal user can access it
→ adb devices worksThey solve different problems.
Reload the host udev rules:
sudo udevadm control --reload-rules
sudo systemctl daemon-reloadNow monitor the service logs:
sudo journalctl -u 'samsung-phone-client-usb@*' -fUnplug the phone and plug it back in.
You should see the remove and add services run.
Then check inside the VM:
lsusb | grep Samsung
adb devicesDid this guide save you time?
Support this site