You can fix the assignment of USB ports with udev rules 
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:
- 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.
- 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.
- Restart the udev service :
Apply the rules by reloading udev:
sudo udevadm control --reload-rules
sudo udevadm trigger
- 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/.
- 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
- 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.