How to enable hot plugging or swapping of serial USB devices in Docker containers
One of the drawbacks of using OctoPrint in the default Docker Container is that the printer must be connected and operational for the container to start. This issue can affect the operation of useful plugins like PSU Control.
Fortunately there is a way to enable serial device hot plugging in a Docker container. It is practical if you are running few printers/containers.
The following instructions were written for 3D printers but the logic could be applied to any serial device and any type of container (HomeAssistant, Zigbee2MQTT, etc...).
This configuration allows the container to start without a serial device plugged and will automatically detect and update the device list when the printer or device is turned on/connected. It was tested in a brand new Raspbian Bullseye 32-bit image (version 11.7) with Docker (24.0.6) from the official website (not the Raspberry PI repository). OctoPrint version for testing was 1.9.2.
1. Create in your host system, the file /etc/udev/rules.d/41-octoprint.docker.rules with the content below. Replace octoprint with the name of your container.
# allow usage/start of the container regardless of whether printer is connected SUBSYSTEM!="tty", GOTO="end_octoprint_printers" ACTION=="add|change", SUBSYSTEM=="tty", KERNEL=="ttyUSB[0-9]|ttyACM[0-9]", RUN+="/usr/bin/docker exec octoprint rm -rf /dev/3dprinter", RUN+="/usr/bin/docker exec octoprint mknod /dev/3dprinter c %M %m" ACTION=="remove", SUBSYSTEM=="tty", KERNEL=="ttyUSB[0-9]|ttyACM[0-9]", RUN+="/usr/bin/docker exec octoprint rm -rf /dev/3dprinter" LABEL="end_octoprint_printers"
3. Activate the udev rules sudo udevadm control --reload-rules && udevadm trigger . There is no need to restart the device.
4. Connect and turn on your printer and video camera.
5. Run stat -c '%n MAJOR: %t MINOR: %T' /dev/* , locate your printer and camera (if any) and take a note of the MAJOR ID. 3D printers are usually listed as /dev/ttyUSB0, /dev/ttyUSB1, /dev/ttyACM0 or /dev/ttyACM1.
6. Convert your MAJOR IDs from the HEX number format to the INT number format ('a6' becomes '166', 'e' becomes '14', 'bc' is '188', etc...). Use any online calculator for that.
7. Edit your docker-compose.yaml:
- Ensure version is 3.6 as cgroup rules behavior changes between docker versions.
- Add your MAJOR integer numbers to the device_cgroup_rules section.
- Comment/delete the privileged permission (if enabled)
- Comment/delete any references to the printer serial port in the devices section (if exist). If you added the cgroup code for the video camera, you can also comment it from devices. If the camera is always on, you can keep the original device mapping.
- Comment/delete any references to the host /dev folders in the volumes section (if exist).
version: '3.6' services: octoprint: image: octoprint/octoprint container_name: octoprint #this should be equal to the udev rules you created restart: unless-stopped #privileged: true ports: - 5000:80 #Map to external port 5000 device_cgroup_rules: #Insert your MAJOR ID numbers below - 'c 166:* rmw' # access to usb serial devices like /dev/ttyUSB0, /dev/ttyUSB1 etc... when using USB serial adapters with the MAJOR of 166 - 'c 188:* rmw' # access to usb serial devices like /dev/ttyACM0, /dev/ttyACM1 etc... when using USB serial adapters with the MAJOR of 188 - 'c 81:* rmw' # access to video and webcam devices like /dev/video0, /dev/video1 etc... when using Logitech webcams with the MAJOR of 81 - 'c 1:3 rw' # access to /dev/null #- 'c 1:5 rw' # access to /dev/zero #- 'c 1:8 rw' # access to /dev/random #- 'c 1:9 rw' # access to /dev/urandom #devices: # use `python -m serial.tools.miniterm` to see what the name is of the printer, this requires pyserial # - /dev/ttyACM0:/dev/ttyACM0 # - /dev/video0:/dev/video0 volumes: - ./octoprint:/octoprint environment: - TZ=America/Sao_Paulo # - ENABLE_MJPG_STREAMER=true #### # uncomment if you wish to edit the configuration files of octoprint # refer to docs on configuration editing for more information #### #config-editor: # image: linuxserver/code-server # ports: # - 8443:8443 # depends_on: # - octoprint # restart: unless-stopped # environment: # - PUID=0 # - PGID=0 # - TZ=America/Chicago # volumes: # - octoprint:/octoprint #volumes: # octoprint:
8. Start your container from the folder where the yaml file is located sudo docker compose up -d.
9. Within Octoprint configuration menu, add /dev/3dprinter to the additional serial ports list. This will enable auto-detection.
10. Do not forget to update the OctoPrint restart command as well: s6-svc -r /var/run/s6/services/octoprint