How to fix USB ports on a mini-PC?

Hello,
this has happened several times already: the USB1 and USB0 ports get swapped on reboot after the circuit breaker has tripped. On USB0 I have my Zigbee USB stick connected and on USB1 my Rflink, and each time the system initializes the Zigbee stick on USB1 and the Rflink on USB0.
I did try to install this config, but on Node-RED when the Rflink is on USB0 it does not connect. So I restore it each time: Zigbee USB stick on USB0 and Rflink on USB1. I already had this problem on the Raspberry Pi (RPI) and @VonOx had previously resolved this issue

docker run -d \
--log-opt max-size=10m \
--restart=always \
--network=host \
--name node_red \
-u node-red:dialout \
--device=/dev/ttyUSB0 \
--device=/dev/ttyUSB1 \
--label com.centurylinklabs.watchtower.enable=false \
-v /var/lib/node-red:/data \
nodered/node-red

Everything is currently working and I’m not keen on reinstalling all of Node-RED with that command.
So I wanted to know if it’s possible to fix the USB port assignments, or do I have to keep swapping the USB ports myself?
Thanks for your help

You can fix the assignment of USB ports with udev rules :slight_smile:

I asked ChatGPT to write a small tutorial, here’s what it gives me (seems pretty good to me):

1. Use udev rules to assign persistent names to USB devices

The /dev/ttyUSB0 and /dev/ttyUSB1 ports are assigned dynamically at system boot, which can cause devices to randomly swap. With udev, you can assign a unique and persistent name to each device based on its characteristics (like product ID, vendor, or serial number).

Here are the steps:

  1. Identify the USB devices :

Use the following command to find information about each device:

udevadm info -q all -n /dev/ttyUSB0

udevadm info -q all -n /dev/ttyUSB1

Look for attributes like ID_VENDOR_ID, ID_MODEL_ID or ID_SERIAL.

  1. Create a udev rule :

Edit or create a file in /etc/udev/rules.d/ (for example /etc/udev/rules.d/99-usb-serial.rules) and add a rule for each device:

SUBSYSTEM==« tty », ATTRS{idVendor}==« XXXX », ATTRS{idProduct}==« YYYY », SYMLINK+=« zigbee_adapter »

SUBSYSTEM==« tty », ATTRS{idVendor}==« ZZZZ », ATTRS{idProduct}==« WWWW », SYMLINK+=« rflink_adapter »

Replace XXXX, YYYY, ZZZZ, and WWWW with the corresponding values found in the previous step.

  1. Restart the udev service :

Apply the rules by reloading udev:

sudo udevadm control --reload-rules

sudo udevadm trigger

  1. Update the Docker command :

Modify your Docker command to point to the new persistent names (/dev/zigbee_adapter and /dev/rflink_adapter):

docker run -d \

–log-opt max-size=10m \

–restart=always \

–network=host \

–name node_red \

-u node-red:dialout \

–device=/dev/zigbee_adapter \

–device=/dev/rflink_adapter \

–label com.centurylinklabs.watchtower.enable=false \

-v /var/lib/node-red:/data \

nodered/node-red

2. Use Docker options to specify devices via their stable path

If you don’t want to use udev, you can specify devices directly via their stable hardware path, available in /dev/serial/by-id/.

  1. List the USB devices :

Run the following command to identify the stable names:

ls -l /dev/serial/by-id/

You will see symbolic links such as:

usb-1a86_USB2.0-Serial-if00-port0 → ../../ttyUSB0

usb-0403_6001_RFLINK-if00-port0 → ../../ttyUSB1

  1. Update the Docker command :

Modify the Docker command to use these paths:

docker run -d \

–log-opt max-size=10m \

–restart=always \

–network=host \

–name node_red \

-u node-red:dialout \

–device=/dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0 \

–device=/dev/serial/by-id/usb-0403_6001_RFLINK-if00-port0 \

–label com.centurylinklabs.watchtower.enable=false \

-v /var/lib/node-red:/data \

nodered/node-red

Summary

Recommended solution: Use udev rules to assign persistent names, which is flexible and independent of Docker.

Quick alternative: Use the /dev/serial/by-id/ paths directly in the Docker command.

These solutions will ensure that your USB devices will no longer swap, even after a reboot or a power outage.