XCP-ng PCIe NIC Passthrough

Virtualization

4 min readAdvancedLast updated Oct 29, 2026

Pass a physical network card (or a single port of one) straight through to a VM on XCP-ng 8.2 / 8.3. The VM gets the real hardware with its own driver, which is ideal for firewall/router VMs (pfSense, OPNsense, VyOS) and for NICs that need features Xen's virtual NICs don't offer.

Before you start

  • In the server's BIOS/UEFI, enable VT-d (Intel) or AMD-Vi / IOMMU (AMD).
  • Make sure you can reach the host's local console or IPMI/iDRAC in case networking changes go wrong.
  • A VM with a passed-through device can't be live-migrated to another host, and snapshots with memory are limited.
1

Find the NIC's PCI address

On the XCP-ng host (SSH or the console in Xen Orchestra):

lspci | grep -i ethernet
03:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
03:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)
04:00.0 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)

Each port of a multi-port card is usually its own function (.0, .1 …) and can be passed through on its own. Write the address in full form with the domain prefix, e.g. 0000:04:00.0.

Check which ports XCP-ng is currently using, so you don't take the management one:

xe pif-list params=device,MAC,management,IP
ls -l /sys/class/net/ | grep -i pci   # maps eth0/eth1… to PCI addresses
2

Hide the NIC from the host

The host (dom0) must release the device before a VM can have it. List every function you want to pass through, each in its own brackets:

/opt/xensource/libexec/xen-cmdline --set-dom0 "xen-pciback.hide=(0000:04:00.0)"
# two ports:
# /opt/xensource/libexec/xen-cmdline --set-dom0 "xen-pciback.hide=(0000:03:00.0)(0000:03:00.1)"

If XCP-ng had already set up the port as a network interface (PIF), remove it from any networks/bonds in Xen Orchestra first. Then reboot the host:

reboot

After the reboot, confirm the device is available for passthrough:

xl pci-assignable-list

Your address (e.g. 0000:04:00.0) should be listed. Any leftover PIF for that port will show as disconnected; you can forget it with xe pif-forget uuid=<pif-uuid>.

3

Attach the NIC to the VM

Shut the VM down, then:

xe vm-list name-label="pfsense" params=uuid      # get the VM's uuid
xe vm-param-set uuid=<vm-uuid> other-config:pci=0/0000:04:00.0

For several devices, separate them with commas:

xe vm-param-set uuid=<vm-uuid> other-config:pci=0/0000:03:00.0,0/0000:03:00.1

Recent versions of Xen Orchestra can also do this from the VM's Advanced tab (Attach PCIs) once the device is hidden from dom0.

Start the VM.

4

Check it inside the VM

  • Linux: lspci | grep -i ethernet and ip link – the real NIC appears next to any Xen virtual NICs.
  • pfSense / OPNsense: the port appears with its real driver name (e.g. igb0, ix0) under Interfaces → Assignments.
  • Windows: install the vendor driver if Device Manager shows an unknown Ethernet controller.

Removing passthrough

# detach from the VM (VM shut down)
xe vm-param-remove uuid=<vm-uuid> param-name=other-config param-key=pci
# give the device back to the host, then reboot
/opt/xensource/libexec/xen-cmdline --delete-dom0 xen-pciback.hide
reboot

Troubleshooting

  • VM won't start: "PCI device not assignable" – the address isn't hidden (check xl pci-assignable-list), or it's typed differently from the lspci output (it must include the 0000: prefix).
  • VM starts but no NIC appears – check VT-d/IOMMU is on: xl dmesg | grep -i -e iommu -e vt-d should show it enabled.
  • The host lost network after the reboot – you hid the management NIC. From the local console run the --delete-dom0 command above, reboot, and pick a different port.
  • You need the NIC shared by several VMs – passthrough gives the whole port to one VM. For sharing, look at the card's SR-IOV support instead.

See also: XCP-ng Intel iGPU Passthrough.

Article information

Category
Virtualization
Read time
4 minutes
Difficulty
Advanced
Last updated
Oct 29, 2026