When adding host device (--device /dev/snd
) to a Docker container, I sometimes encounter Device or resource busy
errors.
I have reproduced the issue with a minimal example involving audio (alsa
). Here's my Dockerfile
(producing an image docker-device-example
) :
FROM debian:buster
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
alsa-utils \
&& rm -rf /var/lib/apt/lists/*
I am running the following command (speaker-test
is a tool to generate a tone that can be used to test the speakers), with /dev/snd
shared :
docker run --rm \
-i -t \
--device /dev/snd \
docker-device-example \
speaker-test
When running the previous command, a pink noise is played, but only under some conditions :
/dev/snd
deviceIt looks like the /dev/snd
is "locked" when used, and if that is the case, I got the following output (the error is represented by the last 2 lines) :
speaker-test 1.1.6
Playback device is default
Stream parameters are 48000Hz, S16_LE, 1 channels
Using 16 octaves of pink noise
ALSA lib pcm_dmix.c:1099:(snd_pcm_dmix_open) unable to open slave
Playback open error: -16,Device or resource busy
And, vice versa, if the pink noise is played (on the container), then I cannot play any sound on my host (Ubuntu). But commands on my host does not fail with the same message. Instead, the command on the host (like aplay test.wav
to play a simple sound) is blocked indefinitely (even when the container is shutdown afterwards).
I tried to debug by running strace aplay test.way
, and the command seems to be blocked on the poll
system call :
poll([{fd=3, events=POLLIN|POLLERR|POLLNVAL}], 1, 4294967295
How can I play sounds from 2 (or more) different containers, or from my host and a container, at the same time?
I've reproduced the issue with /dev/snd
, but I don't know if similar things happen when using other devices, or if it's just related to sound devices or to alsa
.
Note also that when running multiple speaker-test
or aplay
commands simultaneously and all on my host (no containers involved), then all sounds are played.
I can't tell how to solve this with ALSA, but can provide 2 possible ways with pulseaudio. If these setups fail, install pulseaudio
in image to make sure dependencies are fullfilled.
ALSA directly accesses sound hardware and blocks access to it for other clients. But it is possible to set up ALSA to serve multiple clients. That has to be answered by someone else. Probably some ALSA dmix plugin setup is the way to go.
Create pulseaudio socket:
pactl load-module module-native-protocol-unix socket=/tmp/pulseaudio.socket
Create /tmp/pulseaudio.client.conf
for pulseaudio clients:
default-server = unix:/tmp/pulseaudio.socket
# Prevent a server running in the container
autospawn = no
daemon-binary = /bin/true
# Prevent the use of shared memory
enable-shm = false
Share socket and config file with docker and set environment variables PULSE_SERVER
and PULSE_COOKIE
. Container user must be same as on host:
docker run --rm \
--env PULSE_SERVER=unix:/tmp/pulseaudio.socket \
--env PULSE_COOKIE=/tmp/pulseaudio.cookie \
--volume /tmp/pulseaudio.socket:/tmp/pulseaudio.socket \
--volume /tmp/pulseaudio.client.conf:/etc/pulse/client.conf \
--user $(id -u):$(id -g) \
imagename
The cookie will be created by pulseaudio itself.
Get IP address from host:
# either an arbitrary IPv4 address
Hostip="$(ip -4 -o a | awk '{print $4}' | cut -d/ -f1 | grep -v 127.0.0.1 | head -n1)"
# or especially IP from docker daemon
Hostip="$(ip -4 -o a| grep docker0 | awk '{print $4}' | cut -d/ -f1)"
Run docker image. You need a free TCP port, here 34567
is used.
(TCP port number must be in range of cat /proc/sys/net/ipv4/ip_local_port_range
and must not be in use. Check with ss -nlp | grep 34567
.)
docker run --rm \
--name pulsecontainer \
--env PULSE_SERVER=tcp:$Hostip:34567 \
imagename
After docker run
get IP of container with:
Containerip="$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' pulsecontainer)"
Load pulseaudio TCP module authenticated with container IP:
pactl load-module module-native-protocol-tcp port=34567 auth-ip-acl=$Containerip
Be aware that the TCP module is loaded after container is up and running. It takes a moment until pulseaudio server is available for container applications.
If TCP connection fails, check iptables
and ufw
settings.
A How-To summarizing these setups: https://github.com/mviereck/x11docker/wiki/Container-sound:-ALSA-or-Pulseaudio
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With