[GH-ISSUE #269] Audio of some games/apps goes to the wrong sink #187

Closed
opened 2026-10-15 12:57:56 +00:00 by bsakaguchi · 17 comments
Owner

Originally created by @AstronautMikeDexter on GitHub (Oct 13, 2025).
Original GitHub issue: https://github.com/games-on-whales/wolf/issues/269

When launching some games, notably, Victoria 3 and Minecraft, no game audio can be heard in the session in which they are launched.

Instead, the game audio is audible in the wolf-ui main menu.

Workarounds:

  • Some affected games, such as Minecraft, allow the user to select an alternate audio sink for game audio. Changing the audio sink to "Null Output #2" solves this issue.
  • Another workaround is disconnecting the Moonlight client from the Wolf server while leaving the Steam/PrismLauncher/etc. session running, quitting the Wolf UI session, restarting the Wolf UI session, and re-entering the still running Steam/PrismLauncher/etc. session. At this point, all games should be audible inside the proper session.

Please let me know if you need any more information. Thank you.

Originally created by @AstronautMikeDexter on GitHub (Oct 13, 2025). Original GitHub issue: https://github.com/games-on-whales/wolf/issues/269 When launching some games, notably, Victoria 3 and Minecraft, no game audio can be heard in the session in which they are launched. Instead, the game audio is audible in the wolf-ui main menu. **Workarounds:** - Some affected games, such as Minecraft, allow the user to select an alternate audio sink for game audio. Changing the audio sink to "Null Output <span>#</span>2" solves this issue. - Another workaround is disconnecting the Moonlight client from the Wolf server while leaving the Steam/PrismLauncher/etc. session running, quitting the Wolf UI session, restarting the Wolf UI session, and re-entering the still running Steam/PrismLauncher/etc. session. At this point, all games should be audible inside the proper session. Please let me know if you need any more information. Thank you.
bsakaguchi 2026-10-15 12:57:56 +00:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@Pizmovc commented on GitHub (Nov 17, 2025):

I'm having the same issue with the latest stable (with wolf-ui) and your second workaround works for me (I have not tried the first one).

I'm attaching the DEBUG logs for the following actions below:

  • start Wolf
  • connect to Wolf-UI
  • start Steam (sound is working; I can hear the menu sounds)
  • start Dwarf Fortress game (sound is not working; I can't hear Dwarf Fortress)
  • disconnect from Moonlight
  • stop Wolf-UI in Moonlight
  • connect to Wolf-UI
  • connect to Steam/Dwarf Fortress (sound is working; I can hear Dwarf Fortress)

wolf.log

My setup is documented in my blog post, but basically I'm using NixOS, where I manage wolf via systemd using a pre-start script to build the NVidia volume, stop any running wolf containers, etc.

Before updating to wolf-ui this issue only occurred occasionally, now it happens 100% of the time with Dwarf Fortress.

Interestingly, another game that I tried did not have this issue 🤷

<!-- gh-comment-id:3543649900 --> @Pizmovc commented on GitHub (Nov 17, 2025): I'm having the same issue with the latest stable (with wolf-ui) and your second workaround works for me (I have not tried the first one). I'm attaching the DEBUG logs for the following actions below: - start Wolf - connect to Wolf-UI - start Steam (sound is working; I can hear the menu sounds) - start Dwarf Fortress game (sound is not working; I can't hear Dwarf Fortress) - disconnect from Moonlight - stop Wolf-UI in Moonlight - connect to Wolf-UI - connect to Steam/Dwarf Fortress (sound is working; I can hear Dwarf Fortress) [wolf.log](https://github.com/user-attachments/files/23591062/wolf.log) My setup is documented in [my blog post](https://luka.korosec.cc/posts/2025/11/turn-your-server-into-a-family-game-streaming-hub/), but basically I'm using NixOS, where I manage wolf via systemd using a pre-start script to build the NVidia volume, stop any running wolf containers, etc. Before updating to wolf-ui this issue only occurred occasionally, now it happens 100% of the time with Dwarf Fortress. Interestingly, another game that I tried did not have this issue 🤷
Author
Owner

@ABeltramo commented on GitHub (Nov 18, 2025):

After a bit of reading around, I feel that this might be tied to the fact that these games use OpenAL as the audio library.

You can easily control the behaviour of that library by creating a file in the home directory of the retro user called .alsoftrc. You can do that from inside the app whilst streaming with the terminal, via docker exec -it -u retro <APP_CONTAINER_NAME> /bin/bash or externally by looking for the folder under /etc/wolf in your host (or where you've mounted that). I need your help guys (because I don't own either of those games) to experiment with this and to see what are the actual options that we need to set to make them work. Once we got something that works, we can easily bake that file in our GOW images.

These are all the supported options alsoftrc.sample I'd start with something like:

drivers = pulse

[pulse]
allow-moves = true

and see if that changes anything.

In the meantime I'll see if I have any game in my library that uses OpenAL..

<!-- gh-comment-id:3546148582 --> @ABeltramo commented on GitHub (Nov 18, 2025): After a bit of reading around, I feel that this might be tied to the fact that these games use [OpenAL](https://github.com/kcat/openal-soft) as the audio library. You can easily control the behaviour of that library by creating a file in the home directory of the `retro` user called `.alsoftrc`. You can do that from inside the app whilst streaming with the terminal, via `docker exec -it -u retro <APP_CONTAINER_NAME> /bin/bash` or externally by looking for the folder under `/etc/wolf` in your host (or where you've mounted that). I **need your help** guys (because I don't own either of those games) to experiment with this and to see what are the actual options that we need to set to make them work. Once we got something that works, we can easily bake that file in our GOW images. These are all the supported options [alsoftrc.sample](https://github.com/kcat/openal-soft/blob/master/alsoftrc.sample) I'd start with something like: ``` drivers = pulse [pulse] allow-moves = true ``` and see if that changes anything. In the meantime I'll see if I have any game in my library that uses OpenAL..
Author
Owner

@Pizmovc commented on GitHub (Nov 18, 2025):

So I did a bit of digging and tried a few things.

  • Thought it was OpenAL config issue (turned out DF uses FMOD, not OpenAL).
  • Created .asoundrc for ALSA→PulseAudio bridge (wasn't needed, deleted it)
  • Discovered container has two PulseAudio virtual sinks - one with user-ID, one with session-ID (I think?)
  • Fix: pactl move-sink-input 39 9 - moves the application to the other sink
  • After fix, game audio streams to correct sink and works in Moonlight

Should Wolf automatically set container-specific sink as default on startup?

<!-- gh-comment-id:3546978099 --> @Pizmovc commented on GitHub (Nov 18, 2025): So I did a bit of digging and tried a few things. - Thought it was OpenAL config issue (turned out DF uses FMOD, not OpenAL). - Created .asoundrc for ALSA→PulseAudio bridge (wasn't needed, deleted it) - Discovered container has two PulseAudio virtual sinks - one with user-ID, one with session-ID (I think?) - **Fix:** `pactl move-sink-input 39 9` - moves the application to the other sink - After fix, game audio streams to correct sink and works in Moonlight Should Wolf automatically set container-specific sink as default on startup?
Author
Owner

@ABeltramo commented on GitHub (Nov 18, 2025):

Should Wolf automatically set container-specific sink as default on startup?

We already do that by setting the env variables PULSE_SINK, PULSE_SOURCE and PULSE_SERVER to the right values for each running app. Well-behaving clients will pick those up and use the right virtual sink that we setup in Wolf.

The fix that you are proposing will potentially break multi-user because when you do

pactl set-default-sink virtual_sink_<SESSION-ID>

you are actually setting the Pulse Audio server-side default sink.
We need a solution that is per-client, possibly something that we can set in the PA client settings /etc/pulse/client.conf, maybe something like

default-sink = <PULSE_SINK>

would do the trick?

<!-- gh-comment-id:3547103419 --> @ABeltramo commented on GitHub (Nov 18, 2025): > Should Wolf automatically set container-specific sink as default on startup? We already do that by setting the env variables `PULSE_SINK`, `PULSE_SOURCE` and `PULSE_SERVER` to the right values for each running app. Well-behaving clients will pick those up and use the right virtual sink that we setup in Wolf. The fix that you are proposing will potentially break multi-user because when you do ``` pactl set-default-sink virtual_sink_<SESSION-ID> ``` you are actually setting the Pulse Audio server-side default sink. We need a solution that is per-client, possibly something that we can set in the PA client settings `/etc/pulse/client.conf`, maybe something like ``` default-sink = <PULSE_SINK> ``` would do the trick?
Author
Owner

@sikkgit commented on GitHub (Nov 18, 2025):

I am encountering this same issue on every game launched by RPCS3 in Pegasus. I tried both workarounds and they work as described by @AstronautMikeDexter. I tried creating the .alsoftrc file as instructed by @ABeltramo but it didn't change anything.

Edit: Unlike OP, I can't hear the game in the wolf-ui main menu.

<!-- gh-comment-id:3547569535 --> @sikkgit commented on GitHub (Nov 18, 2025): I am encountering this same issue on every game launched by RPCS3 in Pegasus. I tried both workarounds and they work as described by @AstronautMikeDexter. I tried creating the `.alsoftrc` file as instructed by @ABeltramo but it didn't change anything. Edit: Unlike OP, I can't hear the game in the wolf-ui main menu.
Author
Owner

@Pizmovc commented on GitHub (Nov 18, 2025):

We need a solution that is per-client, possibly something that we can set in the PA client settings /etc/pulse/client.conf, maybe something like

default-sink = <PULSE_SINK>

Since the IDs are generated by Wolf, I don't know them beforehand, so I can't mount a file with correct IDs there to test.

And setting the config after container start does nothing, since presumably Steam/Pulse has already read it?

Any other ideas on how I could test this?

Another thing I noted, setting the default Output device in Steam settings to the Session ID sink makes the game sound work properly.

<!-- gh-comment-id:3547725121 --> @Pizmovc commented on GitHub (Nov 18, 2025): > We need a solution that is per-client, possibly something that we can set in the PA client settings /etc/pulse/client.conf, maybe something like >``` >default-sink = <PULSE_SINK> >``` Since the IDs are generated by Wolf, I don't know them beforehand, so I can't mount a file with correct IDs there to test. And setting the config after container start does nothing, since presumably Steam/Pulse has already read it? Any other ideas on how I could test this? Another thing I noted, setting the default Output device in Steam settings to the Session ID sink makes the game sound work properly.
Author
Owner

@ABeltramo commented on GitHub (Nov 18, 2025):

Since the IDs are generated by Wolf, I don't know them beforehand, so I can't mount a file with correct IDs there to test

We set the env variable and you can pick that up before the app starts by adding a script under /etc/cont-init.d/ (ex: https://github.com/games-on-whales/gow/blob/master/images/base/build/overlay/etc/cont-init.d/10-setup_user.sh you'll have to manually add a mount point in your config.toml) that creates the required pulse file.
Something like:

mkdir -p ~/.config/pulse
cat > ~/.config/pulse/client.conf << EOF
default-sink = ${PULSE_SOURCE}
EOF

I've tried a bit to replicate your issue but I can't trigger it on my side in Steam or Lutris for some reason. I'll try with RPCS3 as suggested above..

<!-- gh-comment-id:3548996200 --> @ABeltramo commented on GitHub (Nov 18, 2025): > Since the IDs are generated by Wolf, I don't know them beforehand, so I can't mount a file with correct IDs there to test We set the env variable and you can pick that up before the app starts by adding a script under `/etc/cont-init.d/` (ex: https://github.com/games-on-whales/gow/blob/master/images/base/build/overlay/etc/cont-init.d/10-setup_user.sh you'll have to manually add a mount point in your `config.toml`) that creates the required pulse file. Something like: ```bash mkdir -p ~/.config/pulse cat > ~/.config/pulse/client.conf << EOF default-sink = ${PULSE_SOURCE} EOF ``` I've tried a bit to replicate your issue but I can't trigger it on my side in Steam or Lutris for some reason. I'll try with RPCS3 as suggested above..
Author
Owner

@Pizmovc commented on GitHub (Nov 18, 2025):

Ok, I mounted a script that created a file at ~/.config/pulse/client.conf.

This was the client.conf file after the script ran:

default-sink = virtual_sink_c612d1fc-abdc-45e3-96f9-8fe4f342f76f

Then I also tried setting the /etc/pulse/client.conf file via the same script. In both cases I restarted the container.

In both cases, Dwarf Fortress was outputting sound to the wrong sink and only setting the default sink with pactl set-default-sink (before starting the game) or moving the input with pactl move-sink-input (after starting the game) works.

As per https://linux.die.net/man/5/pulse-client.conf:

default-sink= The default sink to connect to. If specified overwrites the setting in the daemon. The environment variable $PULSE_SINK however takes precedence.

So it makes sense its not working. I guess the game gets the default sink in some other way.


For now, to avoid the manual intervention every time I start the container, I have modified the script to set the default sink on start-up and that fixes it for me. Since I'm the only user ATM, its working for me.

I'll try modifying it to periodically check for sources that are outputting into the wrong sink and move them automatically.
Which I think might actually be the only way to 'fix' misbehaving applications.

<!-- gh-comment-id:3549532091 --> @Pizmovc commented on GitHub (Nov 18, 2025): Ok, I mounted a script that created a file at `~/.config/pulse/client.conf`. This was the `client.conf` file after the script ran: ``` default-sink = virtual_sink_c612d1fc-abdc-45e3-96f9-8fe4f342f76f ``` Then I also tried setting the `/etc/pulse/client.conf` file via the same script. In both cases I restarted the container. In both cases, Dwarf Fortress was outputting sound to the wrong sink and only setting the default sink with `pactl set-default-sink` (before starting the game) or moving the input with `pactl move-sink-input` (after starting the game) works. As per https://linux.die.net/man/5/pulse-client.conf: > default-sink= The default sink to connect to. If specified overwrites the setting in the daemon. The environment variable $PULSE_SINK however takes precedence. So it makes sense its not working. I guess the game gets the default sink in some other way. --- For now, to avoid the manual intervention every time I start the container, I have modified the script to set the default sink on start-up and that fixes it for me. Since I'm the only user ATM, its working for me. I'll try modifying it to periodically check for sources that are outputting into the wrong sink and move them automatically. Which I think might actually be the only way to 'fix' misbehaving applications.
Author
Owner

@ABeltramo commented on GitHub (Nov 18, 2025):

That's a shame and thanks for the link to the man page, at least I know that's not the right path to follow. I think that the apps affected are asking the Pulse server which sink to use and end up using the "global default". We could set that in Wolf via the Pulse socket but it would break multi-user.

Now I'm not sure if there's any way that we can fix that on the client side, but this caught my eye:

Another thing I noted, setting the default Output device in Steam settings to the Session ID sink makes the game sound work properly

Can you check if that changes the default sink for PA like you were doing with pactl or is it some other trick? You should be able to check that with

pacmd list-sources

The * in front of the index indicates the current default input.

<!-- gh-comment-id:3549752383 --> @ABeltramo commented on GitHub (Nov 18, 2025): That's a shame and thanks for the link to the man page, at least I know that's not the right path to follow. I think that the apps affected are asking the Pulse server which sink to use and end up using the "global default". We could set that in Wolf via the Pulse socket but it would break multi-user. Now I'm not sure if there's any way that we can fix that on the client side, but this caught my eye: > Another thing I noted, setting the default Output device in Steam settings to the Session ID sink makes the game sound work properly Can you check if that changes the default sink for PA like you were doing with pactl or is it some other trick? You should be able to check that with ``` pacmd list-sources ``` The `*` in front of the index indicates the current default input.
Author
Owner

@Pizmovc commented on GitHub (Nov 19, 2025):

Tried pacmd first:

❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a bash -c "pacmd list-sources"
No PulseAudio daemon running, or not running as session daemon.

But I think this should be the equivalent with pactl:

# Steam Audio output is set to "Default (auto)"
❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a bash -c "pactl info | grep 'Default Sink'"
Default Sink: virtual_sink_10349179422442916918

# I change the Steam Audio output to virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a
❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a bash -c "pactl info | grep 'Default Sink'"
Default Sink: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a

# I change the Steam Audio output back to "Default (auto)"
❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a bash -c "pactl info | grep 'Default Sink'"
Default Sink: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a

# I change the Steam Audio output to virtual_sink_10349179422442916918
❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a bash -c "pactl info | grep 'Default Sink'"
Default Sink: virtual_sink_10349179422442916918
Here are some additional command outputs ran afterwards
❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl list sources short
1       virtual_sink_10349179422442916918.monitor       module-null-sink.c      s16le 2ch 48000Hz       RUNNING
2       virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a.monitor       module-null-sink.c      s16le 2ch 48000Hz       IDLE

❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl list sources
Source #1
        State: RUNNING
        Name: virtual_sink_10349179422442916918.monitor
        Description: Monitor of Null Output
        Driver: module-null-sink.c
        Sample Specification: s16le 2ch 48000Hz
        Channel Map: front-left,front-right
        Owner Module: 3
        Mute: no
        Volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                balance 0.00
        Base Volume: 65536 / 100% / 0.00 dB
        Monitor of Sink: virtual_sink_10349179422442916918
        Latency: 0 usec, configured 10000 usec
        Flags: DECIBEL_VOLUME LATENCY
        Properties:
                device.description = "Monitor of Null Output"
                device.class = "monitor"
                device.icon_name = "audio-input-microphone"
        Formats:
                pcm

Source #2
        State: IDLE
        Name: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a.monitor
        Description: Monitor of Null Output
        Driver: module-null-sink.c
        Sample Specification: s16le 2ch 48000Hz
        Channel Map: front-left,front-right
        Owner Module: 4
        Mute: no
        Volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                balance 0.00
        Base Volume: 65536 / 100% / 0.00 dB
        Monitor of Sink: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a
        Latency: 0 usec, configured 2000000 usec
        Flags: DECIBEL_VOLUME LATENCY
        Properties:
                device.description = "Monitor of Null Output"
                device.class = "monitor"
                device.icon_name = "audio-input-microphone"
        Formats:
                pcm

❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl info
Server String: /run/user/wolf/pulse-socket
Library Protocol Version: 35
Server Protocol Version: 35
Is Local: yes
Client Index: 24
Tile Size: 65472
User Name: retro
Host Name: e915bb914b67
Server Name: pulseaudio
Server Version: 17.0
Default Sample Specification: s16le 2ch 44100Hz
Default Channel Map: front-left,front-right
Default Sink: virtual_sink_10349179422442916918
Default Source: virtual_sink_10349179422442916918.monitor
Cookie: a4e7:9324
Here are some command outputs with the game running
# I change Steam audio output back to virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a
# I start Dwarf Fortress
❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl list sources short
1       virtual_sink_10349179422442916918.monitor       module-null-sink.c      s16le 2ch 48000Hz       IDLE
2       virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a.monitor       module-null-sink.c      s16le 2ch 48000Hz       RUNNING

❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl list sources
Source #1
        State: IDLE
        Name: virtual_sink_10349179422442916918.monitor
        Description: Monitor of Null Output
        Driver: module-null-sink.c
        Sample Specification: s16le 2ch 48000Hz
        Channel Map: front-left,front-right
        Owner Module: 3
        Mute: no
        Volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                balance 0.00
        Base Volume: 65536 / 100% / 0.00 dB
        Monitor of Sink: virtual_sink_10349179422442916918
        Latency: 0 usec, configured 2000000 usec
        Flags: DECIBEL_VOLUME LATENCY
        Properties:
                device.description = "Monitor of Null Output"
                device.class = "monitor"
                device.icon_name = "audio-input-microphone"
        Formats:
                pcm

Source #2
        State: RUNNING
        Name: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a.monitor
        Description: Monitor of Null Output
        Driver: module-null-sink.c
        Sample Specification: s16le 2ch 48000Hz
        Channel Map: front-left,front-right
        Owner Module: 4
        Mute: no
        Volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                balance 0.00
        Base Volume: 65536 / 100% / 0.00 dB
        Monitor of Sink: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a
        Latency: 0 usec, configured 10000 usec
        Flags: DECIBEL_VOLUME LATENCY
        Properties:
                device.description = "Monitor of Null Output"
                device.class = "monitor"
                device.icon_name = "audio-input-microphone"
        Formats:
                pcm

❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl info
Server String: /run/user/wolf/pulse-socket
Library Protocol Version: 35
Server Protocol Version: 35
Is Local: yes
Client Index: 34
Tile Size: 65472
User Name: retro
Host Name: e915bb914b67
Server Name: pulseaudio
Server Version: 17.0
Default Sample Specification: s16le 2ch 44100Hz
Default Channel Map: front-left,front-right
Default Sink: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a
Default Source: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a.monitor
Cookie: a4e7:9324

❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl list sink-inputs
Sink Input #13
        Driver: protocol-native.c
        Owner Module: 0
        Client: 2
        Sink: 1
        Sample Specification: s16le 2ch 44100Hz
        Channel Map: front-left,front-right
        Format: pcm, format.sample_format = "\"s16le\""  format.rate = "44100"  format.channels = "2"  format.channel_map = "\"front-left,front-right\""
        Corked: no
        Mute: no
        Volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                balance 0.00
        Buffer Latency: 23411 usec
        Sink Latency: 10866 usec
        Resample method: speex-float-1
        Properties:
                media.name = "Sound"
                application.name = "Wolf-UI"
                native-protocol.peer = "UNIX socket client"
                native-protocol.version = "35"
                application.process.id = "1"
                application.process.user = "root"
                application.process.host = "18e263a84214"
                application.process.binary = "wolf-ui"
                application.language = "C"
                application.process.machine_id = "c5ac39c7e4a5482c8dd52db09a486807"

Sink Input #15
        Driver: protocol-native.c
        Owner Module: 0
        Client: 31
        Sink: 2
        Sample Specification: s16le 2ch 48000Hz
        Channel Map: front-left,front-right
        Format: pcm, format.sample_format = "\"s16le\""  format.rate = "48000"  format.channels = "2"  format.channel_map = "\"front-left,front-right\""
        Corked: no
        Mute: no
        Volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                balance 0.00
        Buffer Latency: 46291 usec
        Sink Latency: 2402 usec
        Resample method: n/a
        Properties:
                media.name = "Mixer Stream"
                application.name = "FMOD Audio"
                native-protocol.peer = "UNIX socket client"
                native-protocol.version = "32"
                application.process.id = "1355"
                application.process.user = "retro"
                application.process.host = "75076769a140"
                application.process.binary = "dwarfort"
                application.language = "C"
                window.x11.display = ":0"
                application.process.machine_id = "edb48c8cece14e61bcc1cdc7ddd5011c"

It looks like it does the same thing that I did with pactl set-default-sink <sink>, with Default (auto) being whatever the current default sink is.

<!-- gh-comment-id:3552326420 --> @Pizmovc commented on GitHub (Nov 19, 2025): Tried `pacmd` first: ```shell ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a bash -c "pacmd list-sources" No PulseAudio daemon running, or not running as session daemon. ``` But I think this should be the equivalent with `pactl`: ```shell # Steam Audio output is set to "Default (auto)" ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a bash -c "pactl info | grep 'Default Sink'" Default Sink: virtual_sink_10349179422442916918 # I change the Steam Audio output to virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a bash -c "pactl info | grep 'Default Sink'" Default Sink: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a # I change the Steam Audio output back to "Default (auto)" ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a bash -c "pactl info | grep 'Default Sink'" Default Sink: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a # I change the Steam Audio output to virtual_sink_10349179422442916918 ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a bash -c "pactl info | grep 'Default Sink'" Default Sink: virtual_sink_10349179422442916918 ``` <details> <summary>Here are some additional command outputs ran afterwards</summary> ```shell ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl list sources short 1 virtual_sink_10349179422442916918.monitor module-null-sink.c s16le 2ch 48000Hz RUNNING 2 virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a.monitor module-null-sink.c s16le 2ch 48000Hz IDLE ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl list sources Source #1 State: RUNNING Name: virtual_sink_10349179422442916918.monitor Description: Monitor of Null Output Driver: module-null-sink.c Sample Specification: s16le 2ch 48000Hz Channel Map: front-left,front-right Owner Module: 3 Mute: no Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB balance 0.00 Base Volume: 65536 / 100% / 0.00 dB Monitor of Sink: virtual_sink_10349179422442916918 Latency: 0 usec, configured 10000 usec Flags: DECIBEL_VOLUME LATENCY Properties: device.description = "Monitor of Null Output" device.class = "monitor" device.icon_name = "audio-input-microphone" Formats: pcm Source #2 State: IDLE Name: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a.monitor Description: Monitor of Null Output Driver: module-null-sink.c Sample Specification: s16le 2ch 48000Hz Channel Map: front-left,front-right Owner Module: 4 Mute: no Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB balance 0.00 Base Volume: 65536 / 100% / 0.00 dB Monitor of Sink: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a Latency: 0 usec, configured 2000000 usec Flags: DECIBEL_VOLUME LATENCY Properties: device.description = "Monitor of Null Output" device.class = "monitor" device.icon_name = "audio-input-microphone" Formats: pcm ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl info Server String: /run/user/wolf/pulse-socket Library Protocol Version: 35 Server Protocol Version: 35 Is Local: yes Client Index: 24 Tile Size: 65472 User Name: retro Host Name: e915bb914b67 Server Name: pulseaudio Server Version: 17.0 Default Sample Specification: s16le 2ch 44100Hz Default Channel Map: front-left,front-right Default Sink: virtual_sink_10349179422442916918 Default Source: virtual_sink_10349179422442916918.monitor Cookie: a4e7:9324 ``` </details> <details> <summary>Here are some command outputs with the game running</summary> ```shell # I change Steam audio output back to virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a # I start Dwarf Fortress ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl list sources short 1 virtual_sink_10349179422442916918.monitor module-null-sink.c s16le 2ch 48000Hz IDLE 2 virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a.monitor module-null-sink.c s16le 2ch 48000Hz RUNNING ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl list sources Source #1 State: IDLE Name: virtual_sink_10349179422442916918.monitor Description: Monitor of Null Output Driver: module-null-sink.c Sample Specification: s16le 2ch 48000Hz Channel Map: front-left,front-right Owner Module: 3 Mute: no Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB balance 0.00 Base Volume: 65536 / 100% / 0.00 dB Monitor of Sink: virtual_sink_10349179422442916918 Latency: 0 usec, configured 2000000 usec Flags: DECIBEL_VOLUME LATENCY Properties: device.description = "Monitor of Null Output" device.class = "monitor" device.icon_name = "audio-input-microphone" Formats: pcm Source #2 State: RUNNING Name: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a.monitor Description: Monitor of Null Output Driver: module-null-sink.c Sample Specification: s16le 2ch 48000Hz Channel Map: front-left,front-right Owner Module: 4 Mute: no Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB balance 0.00 Base Volume: 65536 / 100% / 0.00 dB Monitor of Sink: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a Latency: 0 usec, configured 10000 usec Flags: DECIBEL_VOLUME LATENCY Properties: device.description = "Monitor of Null Output" device.class = "monitor" device.icon_name = "audio-input-microphone" Formats: pcm ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl info Server String: /run/user/wolf/pulse-socket Library Protocol Version: 35 Server Protocol Version: 35 Is Local: yes Client Index: 34 Tile Size: 65472 User Name: retro Host Name: e915bb914b67 Server Name: pulseaudio Server Version: 17.0 Default Sample Specification: s16le 2ch 44100Hz Default Channel Map: front-left,front-right Default Sink: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a Default Source: virtual_sink_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a.monitor Cookie: a4e7:9324 ❯ sudo podman exec -u retro WolfSteam_b9a7ad36-6dfe-4fb2-9f6d-a6bcedfd1b9a pactl list sink-inputs Sink Input #13 Driver: protocol-native.c Owner Module: 0 Client: 2 Sink: 1 Sample Specification: s16le 2ch 44100Hz Channel Map: front-left,front-right Format: pcm, format.sample_format = "\"s16le\"" format.rate = "44100" format.channels = "2" format.channel_map = "\"front-left,front-right\"" Corked: no Mute: no Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB balance 0.00 Buffer Latency: 23411 usec Sink Latency: 10866 usec Resample method: speex-float-1 Properties: media.name = "Sound" application.name = "Wolf-UI" native-protocol.peer = "UNIX socket client" native-protocol.version = "35" application.process.id = "1" application.process.user = "root" application.process.host = "18e263a84214" application.process.binary = "wolf-ui" application.language = "C" application.process.machine_id = "c5ac39c7e4a5482c8dd52db09a486807" Sink Input #15 Driver: protocol-native.c Owner Module: 0 Client: 31 Sink: 2 Sample Specification: s16le 2ch 48000Hz Channel Map: front-left,front-right Format: pcm, format.sample_format = "\"s16le\"" format.rate = "48000" format.channels = "2" format.channel_map = "\"front-left,front-right\"" Corked: no Mute: no Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB balance 0.00 Buffer Latency: 46291 usec Sink Latency: 2402 usec Resample method: n/a Properties: media.name = "Mixer Stream" application.name = "FMOD Audio" native-protocol.peer = "UNIX socket client" native-protocol.version = "32" application.process.id = "1355" application.process.user = "retro" application.process.host = "75076769a140" application.process.binary = "dwarfort" application.language = "C" window.x11.display = ":0" application.process.machine_id = "edb48c8cece14e61bcc1cdc7ddd5011c" ``` </details> It looks like it does the same thing that I did with `pactl set-default-sink <sink>`, with `Default (auto)` being whatever the current default sink is.
Author
Owner

@ABeltramo commented on GitHub (Nov 19, 2025):

@Pizmovc thanks for all the info and for all the tests, that's really helpful. I'm not sure how to fix this without breaking multi-user, I have a few ideas but they feel quite complex..

  • Spin up multiple PulseAudio servers, possibly one per app. Annoying but probably the easiest option, should work with lobbies and multiple users
  • Completely ditch PulseAudio and deal with the protocol directly in Wolf. Best option IMHO but time-consuming to implement
  • Switch to something else for audio? Maybe pipewire? I'm sure that would break in other ways..
<!-- gh-comment-id:3554438680 --> @ABeltramo commented on GitHub (Nov 19, 2025): @Pizmovc thanks for all the info and for all the tests, that's really helpful. I'm not sure how to fix this without breaking multi-user, I have a few ideas but they feel quite complex.. - Spin up multiple PulseAudio servers, possibly one per app. Annoying but probably the easiest option, should work with lobbies and multiple users - Completely ditch PulseAudio and deal with the protocol directly in Wolf. Best option IMHO but time-consuming to implement - Switch to something else for audio? Maybe pipewire? I'm sure that would break in other ways..
Author
Owner

@Pizmovc commented on GitHub (Nov 20, 2025):

I am glad I could help.

I don't see other options either. Perhaps a quick workaround, until a better solution is found, is a script that would scan for misbehaving apps and move them to the correct sink. I have created and tested it and its working fine for me.

Setup and monitoring scripts and mounting example
❯ cat ~/wolf-pulse-setup.sh
#!/bin/bash
# PulseAudio monitoring setup for Wolf sessions
# This script runs at container startup via /etc/cont-init.d/

echo "Setting up PulseAudio sink monitoring..."
echo "PULSE_SINK=${PULSE_SINK}"

# Start the monitoring script in the background
nohup /usr/local/bin/wolf-pulse-monitor.sh > /var/log/pulse-monitor.log 2>&1 &

echo "PulseAudio sink monitor started (PID: $!)"

❯ cat ~/wolf-pulse-monitor.sh
#!/bin/bash
# PulseAudio sink monitor for Wolf sessions
# Automatically moves audio streams from wrong sink to correct session-specific sink

echo "Starting PulseAudio sink monitor..."
echo "Target sink: ${PULSE_SINK}"

# Wait for PulseAudio to be ready
for i in {1..10}; do
  if pactl info >/dev/null 2>&1; then
    break
  fi
  echo "Waiting for PulseAudio... ($i/10)"
  sleep 1
done

# Get the correct sink ID for this session
CORRECT_SINK="${PULSE_SINK}"

# Monitor and move streams continuously
while true; do
  # Get all sink inputs and check if they're on the wrong sink
  pactl list sink-inputs short | while read -r input_id sink_id rest; do
    # Get the sink name for this input
    current_sink=$(pactl list sinks short | awk -v id="$sink_id" '$1 == id {print $2}')

    # If it's not our session-specific sink, move it
    if [ "$current_sink" != "$CORRECT_SINK" ] && [ -n "$current_sink" ]; then
      echo "[$(date '+%H:%M:%S')] Moving sink-input #${input_id} from ${current_sink} to ${CORRECT_SINK}"
      pactl move-sink-input "$input_id" "$CORRECT_SINK" 2>/dev/null || true
    fi
  done

  # Check every 2 seconds
  sleep 2
done

❯ sudo podman exec WolfSteam_e36dc771-bb88-4578-ae63-585ec8d10753 cat /var/log/pulse-monitor.log
Starting PulseAudio sink monitor...
Target sink: virtual_sink_e36dc771-bb88-4578-ae63-585ec8d10753
[11:37:03] Moving sink-input #0 from virtual_sink_10349179422442916918 to virtual_sink_e36dc771-bb88-4578-ae63-585ec8d10753
[11:37:50] Moving sink-input #3 from virtual_sink_10349179422442916918 to virtual_sink_e36dc771-bb88-4578-ae63-585ec8d10753

I had to mount the files of course:

        mounts = [
          '/home/luka/wolf-pulse-setup.sh:/etc/cont-init.d/99-pulse-setup.sh:rw',
          '/home/luka/wolf-pulse-monitor.sh:/usr/local/bin/wolf-pulse-monitor.sh:rw'
        ]
        name = 'WolfSteam'

Its important that you don't mount wolf-pulse-monitor.sh into /etc/cont-init.d/ since it will be run directly (not in the background) and will block the startup process of the container.

<!-- gh-comment-id:3557580928 --> @Pizmovc commented on GitHub (Nov 20, 2025): I am glad I could help. I don't see other options either. Perhaps a quick workaround, until a better solution is found, is a script that would scan for misbehaving apps and move them to the correct sink. I have created and tested it and its working fine for me. <details> <summary>Setup and monitoring scripts and mounting example</summary> ```shell ❯ cat ~/wolf-pulse-setup.sh #!/bin/bash # PulseAudio monitoring setup for Wolf sessions # This script runs at container startup via /etc/cont-init.d/ echo "Setting up PulseAudio sink monitoring..." echo "PULSE_SINK=${PULSE_SINK}" # Start the monitoring script in the background nohup /usr/local/bin/wolf-pulse-monitor.sh > /var/log/pulse-monitor.log 2>&1 & echo "PulseAudio sink monitor started (PID: $!)" ❯ cat ~/wolf-pulse-monitor.sh #!/bin/bash # PulseAudio sink monitor for Wolf sessions # Automatically moves audio streams from wrong sink to correct session-specific sink echo "Starting PulseAudio sink monitor..." echo "Target sink: ${PULSE_SINK}" # Wait for PulseAudio to be ready for i in {1..10}; do if pactl info >/dev/null 2>&1; then break fi echo "Waiting for PulseAudio... ($i/10)" sleep 1 done # Get the correct sink ID for this session CORRECT_SINK="${PULSE_SINK}" # Monitor and move streams continuously while true; do # Get all sink inputs and check if they're on the wrong sink pactl list sink-inputs short | while read -r input_id sink_id rest; do # Get the sink name for this input current_sink=$(pactl list sinks short | awk -v id="$sink_id" '$1 == id {print $2}') # If it's not our session-specific sink, move it if [ "$current_sink" != "$CORRECT_SINK" ] && [ -n "$current_sink" ]; then echo "[$(date '+%H:%M:%S')] Moving sink-input #${input_id} from ${current_sink} to ${CORRECT_SINK}" pactl move-sink-input "$input_id" "$CORRECT_SINK" 2>/dev/null || true fi done # Check every 2 seconds sleep 2 done ❯ sudo podman exec WolfSteam_e36dc771-bb88-4578-ae63-585ec8d10753 cat /var/log/pulse-monitor.log Starting PulseAudio sink monitor... Target sink: virtual_sink_e36dc771-bb88-4578-ae63-585ec8d10753 [11:37:03] Moving sink-input #0 from virtual_sink_10349179422442916918 to virtual_sink_e36dc771-bb88-4578-ae63-585ec8d10753 [11:37:50] Moving sink-input #3 from virtual_sink_10349179422442916918 to virtual_sink_e36dc771-bb88-4578-ae63-585ec8d10753 ``` I had to mount the files of course: ```toml mounts = [ '/home/luka/wolf-pulse-setup.sh:/etc/cont-init.d/99-pulse-setup.sh:rw', '/home/luka/wolf-pulse-monitor.sh:/usr/local/bin/wolf-pulse-monitor.sh:rw' ] name = 'WolfSteam' ``` Its important that you don't mount `wolf-pulse-monitor.sh` into `/etc/cont-init.d/` since it will be run directly (not in the background) and will block the startup process of the container. </details>
Author
Owner

@linxuhao commented on GitHub (Jan 5, 2026):

I got gemini pro implement a PID based watch dog script, works good with my 2 client test.
Below the code I added in /opt/gow/launch-comp.sh

#after source /opt/gow/bash-lib/utils.sh
# =================================================================
# PID-BASED AUDIO WATCHDOG (ROBUST)
# =================================================================
monitor_audio() {
  if [ -z "$PULSE_SINK" ]; then
    echo "[Audio Watchdog] No PULSE_SINK set, exiting monitor."
    return
  fi

  echo "[Audio Watchdog] Started. monitoring for local PIDs..."

  # We use a loop that runs every 2 seconds.
  # This is safer than 'pactl subscribe' because parsing multi-line
  # output from a continuous pipe is very prone to errors in Bash.
  while true; do
      # Get the full list of inputs
      # We parse it line by line to pair Input IDs with PIDs
      CURRENT_ID=""

      pactl list sink-inputs | while read -r line; do
          # 1. Catch the Input ID line (e.g., "Sink Input #13")
          if [[ "$line" =~ Sink\ Input\ \#([0-9]+) ]]; then
              CURRENT_ID="${BASH_REMATCH[1]}"

          # 2. Catch the PID line (e.g., application.process.id = "123")
          elif [[ "$line" =~ application.process.id\ =\ \"([0-9]+)\" ]]; then
              PID="${BASH_REMATCH[1]}"

              # 3. CRITICAL CHECK: Does this PID exist in THIS container?
              if [ -d "/proc/$PID" ] && [ -n "$CURRENT_ID" ]; then
                  # It is OUR process. Now check if it's on the wrong sink.
                  # (We query just this input to be efficient)
                  CURRENT_SINK=$(pactl list sink-inputs | grep -A 5 "Sink Input #$CURRENT_ID" | grep "Sink:" | awk '{print $2}')

                  if [ "$CURRENT_SINK" != "$PULSE_SINK" ]; then
                      echo "[Audio Watchdog] Claiming Stream ID $CURRENT_ID (PID $PID) -> $PULSE_SINK"
                      pactl move-sink-input "$CURRENT_ID" "$PULSE_SINK" 2>/dev/null || true
                  fi
              fi
          fi
      done

      sleep 2
  done
}
# =================================================================

then in

launcher() {
  #existing things 

  # Standard Env Vars (still good practice)
  if [ -n "$PULSE_SINK" ]; then
    export SDL_AUDIODRIVER=pulse
    export SDL_AUDIODEVICE="$PULSE_SINK"
    export PULSE_SINK
    export PULSE_SOURCE
    export ALSOFT_DRIVERS=pulse
    export AUDIODEV="$PULSE_SINK"
    gow_log "AUDIO CONFIG: Global audio variables set for PulseAudio sink: $PULSE_SINK"
  fi

  # Start the Watchdog
  monitor_audio &
  WATCHDOG_PID=$!
  trap "kill $WATCHDOG_PID" EXIT

  #existing things 
}

I made some "hack" on the container pid level (so since every container has their own pid, does not break multi-user experience using pactl move-sink-input):
then

         mounts = [
            #otherMounts
            'path_to/launch-comp-fixed.sh:/opt/gow/launch-comp.sh:ro'
        ]

with above content injected to launch-comp-fixed.sh:

To get the content of the /opt/gow/launch-comp.sh (so you can create a "fixed" version of it by copy all the content in a new file, and do as suggested above)
if you are using podman
sudo podman run --rm --entrypoint cat ghcr.io/games-on-whales/steam:edge /opt/gow/launch-comp.sh
or if you use docker
docker run --rm --entrypoint cat ghcr.io/games-on-whales/steam:edge /opt/gow/launch-comp.sh

Maybe we can integrate the fix so people don't have to manually update /opt/gow/launch-comp.sh each time they update the images

<!-- gh-comment-id:3708589141 --> @linxuhao commented on GitHub (Jan 5, 2026): I got gemini pro implement a PID based watch dog script, works good with my 2 client test. Below the code I added in /opt/gow/launch-comp.sh ``` #after source /opt/gow/bash-lib/utils.sh # ================================================================= # PID-BASED AUDIO WATCHDOG (ROBUST) # ================================================================= monitor_audio() { if [ -z "$PULSE_SINK" ]; then echo "[Audio Watchdog] No PULSE_SINK set, exiting monitor." return fi echo "[Audio Watchdog] Started. monitoring for local PIDs..." # We use a loop that runs every 2 seconds. # This is safer than 'pactl subscribe' because parsing multi-line # output from a continuous pipe is very prone to errors in Bash. while true; do # Get the full list of inputs # We parse it line by line to pair Input IDs with PIDs CURRENT_ID="" pactl list sink-inputs | while read -r line; do # 1. Catch the Input ID line (e.g., "Sink Input #13") if [[ "$line" =~ Sink\ Input\ \#([0-9]+) ]]; then CURRENT_ID="${BASH_REMATCH[1]}" # 2. Catch the PID line (e.g., application.process.id = "123") elif [[ "$line" =~ application.process.id\ =\ \"([0-9]+)\" ]]; then PID="${BASH_REMATCH[1]}" # 3. CRITICAL CHECK: Does this PID exist in THIS container? if [ -d "/proc/$PID" ] && [ -n "$CURRENT_ID" ]; then # It is OUR process. Now check if it's on the wrong sink. # (We query just this input to be efficient) CURRENT_SINK=$(pactl list sink-inputs | grep -A 5 "Sink Input #$CURRENT_ID" | grep "Sink:" | awk '{print $2}') if [ "$CURRENT_SINK" != "$PULSE_SINK" ]; then echo "[Audio Watchdog] Claiming Stream ID $CURRENT_ID (PID $PID) -> $PULSE_SINK" pactl move-sink-input "$CURRENT_ID" "$PULSE_SINK" 2>/dev/null || true fi fi fi done sleep 2 done } # ================================================================= ``` then in ``` launcher() { #existing things # Standard Env Vars (still good practice) if [ -n "$PULSE_SINK" ]; then export SDL_AUDIODRIVER=pulse export SDL_AUDIODEVICE="$PULSE_SINK" export PULSE_SINK export PULSE_SOURCE export ALSOFT_DRIVERS=pulse export AUDIODEV="$PULSE_SINK" gow_log "AUDIO CONFIG: Global audio variables set for PulseAudio sink: $PULSE_SINK" fi # Start the Watchdog monitor_audio & WATCHDOG_PID=$! trap "kill $WATCHDOG_PID" EXIT #existing things } ``` > I made some "hack" on the container pid level (so since every container has their own pid, does not break multi-user experience using pactl move-sink-input): > then > ``` > mounts = [ > #otherMounts > 'path_to/launch-comp-fixed.sh:/opt/gow/launch-comp.sh:ro' > ] > ``` > with above content injected to launch-comp-fixed.sh: > > To get the content of the /opt/gow/launch-comp.sh (so you can create a "fixed" version of it by copy all the content in a new file, and do as suggested above) > if you are using podman > `sudo podman run --rm --entrypoint cat ghcr.io/games-on-whales/steam:edge /opt/gow/launch-comp.sh` > or if you use docker > `docker run --rm --entrypoint cat ghcr.io/games-on-whales/steam:edge /opt/gow/launch-comp.sh` > > **Maybe we can integrate the fix so people don't have to manually update /opt/gow/launch-comp.sh each time they update the images**
Author
Owner

@camelcx commented on GitHub (Feb 2, 2026):

I was building a sober image and came across this problem. The watchdog solution above should work but I hate polling... So I found that we could use "pactl subscribe" to avoid polling. And we could just check the host instead of PID (move all input from matching host to $PULSE_SINK). I had the following script run in the background before the launch command in the startup-app.sh. This works for me. Again, if the game honours $PULSE_SINK, it would be much nicer...

#!/usr/bin/env bash
set -euo pipefail

: "${PULSE_SINK:?PULSE_SINK is not set}"
TARGET_SINK="$PULSE_SINK"
SELF_HOST="$(hostname)"

log() { echo "[pulse-auto-move] $*" >&2; }

move_matching_host_streams() {
  # Parse sink-input blocks; whenever host matches, move that sink-input
  local id="" host=""

  while IFS= read -r line; do
    if [[ "$line" =~ ^Sink\ Input\ \#([0-9]+) ]]; then
      # New block begins
      id="${BASH_REMATCH[1]}"
      host=""
      continue
    fi

    if [[ "$line" =~ application\.process\.host\ =\ \"([^\"]+)\" ]]; then
      host="${BASH_REMATCH[1]}"

      if [[ -n "$id" && "$host" == "$SELF_HOST" ]]; then
        log "Moving sink-input #$id (host=$host) -> $TARGET_SINK"
        pactl move-sink-input "$id" "$TARGET_SINK" || true
      fi
    fi
  done < <(pactl list sink-inputs)
}

log "Starting (self_host=$SELF_HOST, target_sink=$TARGET_SINK)"

# Catch any already-existing streams
move_matching_host_streams

# Event-driven updates 
pactl subscribe | while IFS= read -r ev; do
  case "$ev" in
    *"sink-input"*) move_matching_host_streams ;;
  esac
done
<!-- gh-comment-id:3837627329 --> @camelcx commented on GitHub (Feb 2, 2026): I was building a sober image and came across this problem. The watchdog solution above should work but I hate polling... So I found that we could use "pactl subscribe" to avoid polling. And we could just check the host instead of PID (move all input from matching host to $PULSE_SINK). I had the following script run in the background before the launch command in the startup-app.sh. This works for me. Again, if the game honours $PULSE_SINK, it would be much nicer... ``` #!/usr/bin/env bash set -euo pipefail : "${PULSE_SINK:?PULSE_SINK is not set}" TARGET_SINK="$PULSE_SINK" SELF_HOST="$(hostname)" log() { echo "[pulse-auto-move] $*" >&2; } move_matching_host_streams() { # Parse sink-input blocks; whenever host matches, move that sink-input local id="" host="" while IFS= read -r line; do if [[ "$line" =~ ^Sink\ Input\ \#([0-9]+) ]]; then # New block begins id="${BASH_REMATCH[1]}" host="" continue fi if [[ "$line" =~ application\.process\.host\ =\ \"([^\"]+)\" ]]; then host="${BASH_REMATCH[1]}" if [[ -n "$id" && "$host" == "$SELF_HOST" ]]; then log "Moving sink-input #$id (host=$host) -> $TARGET_SINK" pactl move-sink-input "$id" "$TARGET_SINK" || true fi fi done < <(pactl list sink-inputs) } log "Starting (self_host=$SELF_HOST, target_sink=$TARGET_SINK)" # Catch any already-existing streams move_matching_host_streams # Event-driven updates pactl subscribe | while IFS= read -r ev; do case "$ev" in *"sink-input"*) move_matching_host_streams ;; esac done ```
Author
Owner

@ABeltramo commented on GitHub (Feb 7, 2026):

This should be fixed now in the latest wolf:stable, it would be nice if anyone from this thread can check the latest image out and report if it's correctly working for any of the app involved in this discussion.

<!-- gh-comment-id:3864104566 --> @ABeltramo commented on GitHub (Feb 7, 2026): This should be fixed now in the latest `wolf:stable`, it would be nice if anyone from this thread can check the latest image out and report if it's correctly working for any of the app involved in this discussion.
Author
Owner

@linxuhao commented on GitHub (Feb 7, 2026):

I indeed see below new system in log, but I see all audio sinks are moved to one client, one client got "muted", the other display all sounds, exactly like when I force set default audio sink to the pulse audio server

20:49:04.735029490 INFO  | [PULSE_ROUTER] Setup complete (handlers registered, PA subscribed)
20:49:04.735767898 DEBUG | [PULSE] Pulse connection ready
20:49:04.735892590 DEBUG | [PULSE_ROUTER] Enabled PulseAudio subscription (sink-input + sink)
<!-- gh-comment-id:3865415712 --> @linxuhao commented on GitHub (Feb 7, 2026): I indeed see below new system in log, but I see all audio sinks are moved to one client, one client got "muted", the other display all sounds, exactly like when I force set default audio sink to the pulse audio server ``` 20:49:04.735029490 INFO | [PULSE_ROUTER] Setup complete (handlers registered, PA subscribed) 20:49:04.735767898 DEBUG | [PULSE] Pulse connection ready 20:49:04.735892590 DEBUG | [PULSE_ROUTER] Enabled PulseAudio subscription (sink-input + sink) ```
Author
Owner

@ABeltramo commented on GitHub (Feb 7, 2026):

Can you post the full logs? That doesn't really highlight any issue

<!-- gh-comment-id:3865433232 --> @ABeltramo commented on GitHub (Feb 7, 2026): Can you post the full logs? That doesn't really highlight any issue
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
games-on-whales/wolf#187
No description provided.