3 min read

Bluetooth Passthrough to VM in Proxmox (and into Docker)

Bluetooth Passthrough to VM in Proxmox (and into Docker)
Photo by Denny Müller / Unsplash

The plan was to gather data from wireless Bluetooth temperature sensors (RuuviTag) to InfluxDB and visualize in Grafana. I will go through this set up in a later post. However before setting up containers, databases or visualizations, we have to set up a VM in Proxmox which has the visibility to the hosts Bluetooth adapter.

In this example I'm using Intel AX201 WIFI/Bluetooth adapter running on Intel Alder Lake N100 MiniPC (Chuwi Larkbox X). Find complete specifications for the environment below:

Proxmox:

  • Intel AX201 WIFI/Bluetooth adapter
  • Proxmox 8.0.4
  • Kernel 6.2.16-19-pve

Virtual Machine:

  • Ubuntu 22.04.4 LTS
  • Kernel 5.15.0-107-generic
  • Docker version 26.1.3

Proxmox Configuration

First and foremost we have to find out the Bluetooth adapters device ID in the Proxmox host. We know that the AX201 has a PCIe connected WiFi adapter and the Bluetooth is internally connected as a USB-device.

# Login as a root in Proxmox node
$ ssh [email protected]

# As the device is a USB device (although built in), we have to find the Bluetooth adapters device ID
root@pve1:~# lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 002: ID 8087:0026 Intel Corp. AX201 Bluetooth    <<-----
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Next (if you don't already know) we must find out the ID of the VM to passthrough the adapter to.

root@pve1:~# qm list
      VMID NAME                 STATUS     MEM(MB)    BOOTDISK(GB) PID
       101 docker-pve1-2        running    2048              32.00 2432166

Then we will passthrough the Bluetooth adapter (device ID) to the VM (VM ID).

qm set 101 -usb0 host=8087:0026

On Proxmox side, this is all that has to be done.

Ubuntu VM Configuration

After the Proxmox host configuration we will finish the configuration on the VM side.

# Log into the VM as a root
$ ssh [email protected]
admin@docker-pve1-2:~$ sudo su -

# Check that the host can see the Bluetooth adapter
root@docker-pve1-2:~# dmesg | grep Bluetooth
[    4.689354] Bluetooth: Core ver 2.22
[    4.689376] Bluetooth: HCI device and connection manager initialized
[    4.689379] Bluetooth: HCI socket layer initialized
[    4.689381] Bluetooth: L2CAP socket layer initialized
[    4.689383] Bluetooth: SCO socket layer initialized
[    4.812081] Bluetooth: hci0: Firmware timestamp 2022.51 buildtype 1 build 56683
[    4.818893] Bluetooth: hci0: Found device firmware: intel/ibt-0040-1050.sfi
[    4.818911] Bluetooth: hci0: Boot Address: 0x100800
[    4.818913] Bluetooth: hci0: Firmware Version: 107-51.22
[    4.818914] Bluetooth: hci0: Firmware already loaded

At this point you will most likely get an error in the dmesg output (the output above is what it should look like with no errors). This is due to the fact that there is no correct firmware file available for the AX201 adapter. The error in dmesg output will look like something below.

Bluetooth: hci0: Failed to load Intel firmware file

This can be fixed by copying, or even better, making a symbolic link to existing firmware file that works fine with the AX201 adapter.

# cd /usr/lib/firmware/intel
root@docker-pve1-2:/usr/lib/firmware/intel# ln -s ibt-1040-4150.ddc ibt-0040-1050.ddc
root@docker-pve1-2:/usr/lib/firmware/intel# ln -s ibt-1040-4150.sfi ibt-0040-1050.sfi

After these steps and a reboot of the VM, the VM will have a working Bluetooth adapter.

Docker Configuration

In the Docker Compose file we need to provide some additional configurations to make the Docker container utilize the Bluetooth adapter. The example below is not a complete compose file, just some additional needed parameters.

services:
  servicename:
    image: docker_image
    container_name: test
    cap_add:
      - NET_ADMIN
    network_mode: host

Running the same directly from the command line would translate as below.

docker run --net=host --cap-add=NET_ADMIN --name test -d docker_image

The flag --net=host (network_mode: host in the docker compose file) allows us to use the network adapters (including Bluetooth) natively.

Whereas the --cap-add=NET_ADMIN flag (cap_add: -NET_ADMIN in the docker compose file) is needed for the container to be able to modify the network interfaces (including Bluetooth).

Now when running the Docker container you should have visibility to the Bluetooth adapter and ability to access it.