dockerdocker-composetty

docker-compose devices map all devices from local to container


I have this docker-compose service that i am trying to make work.

  app:
    build: ./
    volumes:
      - .:/app:delegated
    devices:
      - ${DEVICE:-/dev/null}:-/dev/ttyUSB*

Response when running docker-compose up app

Error response from daemon: error gathering device information while adding custom device "/dev/ttyUSB*": no such file or directory

The idea here is to get all devices that match /dev/ttyUSB* from local and create them inside the container. This way i can have 0-N devices in the container, handling scenarios when no devices are connected. Also I do not have to know each device's tty<id>

I have read some old answers like this one : mapping all available devices in docker-compose but these either use

--privileged or do not answer the question of how to get (all | no devices) copied and accessible.

I am aware that:

app:
    build: ./
    volumes:
      - .:/app:delegated
    devices:
      - ${DEVICE:-/dev/null}:-/dev/ttyUSB0

works but not when the device is assigned a different number, hence the need to map them all.


Solution

  • docker-compose able to read YAML from STDIN so just generate it on the fly.

    Let's say that our original file is:

    abc:
      container_name: abc
      image: alpine
      entrypoint: ["watch", "-n", "5", "ls"]
    

    And devices are:

    $ ls -l /dev/ttyS*
    crw-rw-rw- 1 root root 4, 64 Nov 11 09:34 /dev/ttyS0
    crw-rw-rw- 1 root root 4, 65 Nov 11 09:34 /dev/ttyS1
    crw-rw-rw- 1 root root 4, 66 Nov 11 09:34 /dev/ttyS2
    crw-rw-rw- 1 root root 4, 67 Nov 11 09:34 /dev/ttyS3
    

    So, let's add these device to YAML:

    $ printf '%s\n%s' "$(cat docker-compose.yml)" "$(if [ "$(ls /dev/ttyS*)x" != "x" ]; then echo -e "  devices:"; for D in /dev/ttyS*; do echo "    - ${D}:${D//ttyS/ttyUSB}"; done; fi )"
    

    will print:

    abc:
      container_name: abc
      image: alpine
      entrypoint: ["watch", "-n", "5", "ls"]
      devices:
        - /dev/ttyS0:/dev/ttyUSB0
        - /dev/ttyS1:/dev/ttyUSB1
        - /dev/ttyS2:/dev/ttyUSB2
        - /dev/ttyS3:/dev/ttyUSB3
    

    Now let's try to run docker container using STDIN:

    $ printf '%s\n%s' "$(cat docker-compose.yml)" "$(if [ "$(ls /dev/ttyS*)x" != "x" ]; then echo -e "  devices:"; for D in /dev/ttyS*; do echo "    - ${D}:${D//ttyS/ttyUSB}"; done; fi )" | docker-compose -f /dev/stdin -f docker-compose.yml up -d
    
    Creating abc ... done
    

    Let's attach to container and check devices:

    $ docker exec -it abc sh
    # ls -l /dev
    ...
    crw-rw-rw-    1 root     root        4,  64 Nov 11 11:06 ttyUSB0
    crw-rw-rw-    1 root     root        4,  65 Nov 11 11:06 ttyUSB1
    crw-rw-rw-    1 root     root        4,  66 Nov 11 11:06 ttyUSB2
    crw-rw-rw-    1 root     root        4,  67 Nov 11 11:06 ttyUSB3
    ...
    

    Profit

    Also, you can check this answer