Docker Permission Denied When Accessing Host USB or Serial Devices
Quick answer
A container failing to access a USB or serial device with a permission error usually means either the device wasn't explicitly passed into the container at...
A container failing to access a USB or serial device with a permission error usually means either the device wasn't explicitly passed into the container at all, or it was passed through correctly but the container's own internal user lacks the necessary group membership or permissions to actually use it, since device access requires both steps to be correctly configured together.
The Problem
A container running an application that needs to communicate with a physical device (an Arduino, a USB serial adapter, a specialized hardware interface) fails:
PermissionError: [Errno 13] Permission denied: '/dev/ttyUSB0'
Error: Could not open device: Permission denied
The same application, run directly on the host (outside any container), might work fine, pointing at something specific to the container's access to the device rather than the device or application itself being broken.
Why It Happens
By default, Docker containers have no access whatsoever to host devices — this needs to be explicitly granted, and even once granted, Linux's standard permission model (which device files also follow) still applies, meaning the process inside the container also needs appropriate group membership or permissions to actually use a device it can technically see:
- The device wasn't passed into the container at all via the
--deviceflag or Compose'sdeviceskey, so it's simply not accessible from inside the container's filesystem namespace regardless of any in-container permission configuration - The device was correctly passed in, but the container's internal user isn't a member of the group that owns the device file (commonly
dialoutfor serial devices on many Linux distributions), so standard Unix permission checks correctly deny access - The host's own device file permissions themselves are more restrictive than expected, requiring the host-level permissions to also be addressed before any container-level configuration can meaningfully help
The Fix
First, confirm the device is actually being passed into the container using Docker's --device flag:
docker run --device=/dev/ttyUSB0 your-image
In Docker Compose:
services:
app:
image: your-image
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0"
With the device correctly mapped, verify it's actually visible from inside the container:
docker exec -it your-container ls -la /dev/ttyUSB0
If the device appears but access is still denied, check the group ownership shown in that output, and confirm the container's process is running as a user that's a member of the corresponding group:
docker exec -it your-container groups
# confirm this includes the group shown as owning /dev/ttyUSB0 (often "dialout")
If the container's user isn't in the appropriate group, add it within your Dockerfile, or run the container with an explicit group assignment matching the host device's group ownership:
FROM ubuntu:22.04
RUN groupadd -f dialout && usermod -aG dialout your-app-user
Alternatively, run the container with an explicitly matching group ID, which is often simpler than modifying the Dockerfile, particularly useful if the group ID varies across different host machines:
docker run --device=/dev/ttyUSB0 --group-add $(getent group dialout | cut -d: -f3) your-image
This dynamically looks up the host's actual dialout group ID and adds the container's process to that same group ID, correctly aligning permissions regardless of what the specific numeric group ID happens to be on any particular host.
For the simplest (though least granular and generally not recommended for anything beyond controlled, trusted local development scenarios) approach, run the container in privileged mode, which grants broad access to host devices without needing individual device mapping or group configuration — but be aware this significantly expands the container's overall capabilities well beyond just device access, which is a meaningful security tradeoff to understand before using:
docker run --privileged your-image
Reserve privileged mode for genuinely trusted, controlled contexts (local development, dedicated hardware-interfacing systems) rather than as a default, casual fix for device permission issues, since it removes much of the isolation Docker containers are otherwise designed to provide.
Still Not Working?
If group-based access still doesn't resolve the issue, check whether the host itself has restrictive udev rules governing the specific device, since some devices have custom udev rules (particularly common for specialized hardware) that set narrower permissions than the general group-based model alone would suggest:
udevadm info -a -n /dev/ttyUSB0 | grep -i mode
If a specific udev rule is found to be more restrictive than expected, addressing it at the host level (adjusting or adding an appropriate udev rule for the specific device) may be necessary before container-level device mapping and group configuration alone can succeed, since a restrictive host-level rule takes precedence regardless of how correctly the container side is configured.