Skip to main content

Audio Support

aibox can enable audio support for voice-capable tools. Audio is bridged from the container to the host via PulseAudio over TCP, and the required container packages live in the optional audio-voice addon.

Why Audio Matters

Claude Code supports voice interaction. For this to work inside a container, audio output (and optionally input) must be forwarded to the host's sound system. aibox handles this by installing PulseAudio client utilities in the container and connecting them to a PulseAudio server running on the host.

Architecture

Container Host
┌─────────────────────┐ ┌─────────────────────┐
│ Claude Code │ │ PulseAudio Server │
│ │ │ │ │ │
│ pulseaudio-utils │────>│ TCP :4714 │
│ sox │ │ │ │
│ .asoundrc │ │ Speakers / Mic │
└─────────────────────┘ └─────────────────────┘

The container sets PULSE_SERVER to point at the host's PulseAudio TCP module. Audio data flows over the network socket.

Configuration in aibox.toml

[audio]
enabled = true
backend = "pulseaudio"
install = true
pulse_server = "tcp:host.docker.internal:4714"
FieldDefaultDescription
enabledfalseWhether to set up audio environment variables in the container
backendpulseaudioAudio bridge backend. Only PulseAudio is currently supported
installtrueWhether to install the internal audio-voice tool recipe when audio is enabled
pulse_servertcp:host.docker.internal:4714PulseAudio server address

When enabled = true, the generated docker-compose.yml sets PULSE_SERVER in the container environment. When install = true, it also selects the internal audio-voice recipe during aibox apply, which installs Sox, PulseAudio client utilities, and ALSA PulseAudio plugins in the container.

Host Setup

The fastest way to set up audio on your host is the built-in CLI command:

# Check if your host is ready
aibox doctor audio

# Automatic setup (macOS: installs PulseAudio, configures TCP, creates launchd agent)
aibox apply audio

aibox apply audio handles:

  • Installing PulseAudio via Homebrew (macOS) if not present
  • Configuring ~/.config/pulse/default.pa with the TCP module on port 4714
  • Creating a launchd agent with KeepAlive so PulseAudio auto-starts and restarts on crash (macOS)
  • Loading the TCP module immediately

aibox doctor audio diagnoses: PulseAudio installation, daemon status, TCP module, persistence config, port listening, launchd agent (macOS), and connectivity.

Both commands accept --port to override the default port (4714).

Manual setup

If you prefer manual configuration:

macOS

  1. Install PulseAudio:

    brew install pulseaudio
  2. Enable the TCP module. Add to ~/.config/pulse/default.pa:

    load-module module-native-protocol-tcp port=4714 auth-anonymous=1
  3. Start PulseAudio:

    pulseaudio --start
  4. Verify it is listening:

    lsof -i :4714

Docker Desktop and OrbStack provide host.docker.internal automatically. For Podman, check your machine's network configuration — you may need to use the host IP directly:

[audio]
enabled = true
pulse_server = "tcp:192.168.64.1:4714"

Linux

  1. PulseAudio is likely already running. Enable the TCP module:

    pactl load-module module-native-protocol-tcp port=4714 auth-ip-acl=127.0.0.1;172.16.0.0/12;10.0.0.0/8;192.168.0.0/16

    To make this persistent, add to ~/.config/pulse/default.pa:

    load-module module-native-protocol-tcp port=4714 auth-ip-acl=127.0.0.1;172.16.0.0/12;10.0.0.0/8;192.168.0.0/16
  2. Use host.docker.internal (Docker 20.10+) or the Docker bridge IP:

    [audio]
    enabled = true
    pulse_server = "tcp:host.docker.internal:4714"
Firewall

Ensure port 4714 is accessible from the container network. On systems with strict firewalls, you may need to allow traffic from the Docker/Podman bridge interface.

Claude Code OAuth in Containers

When running claude auth inside a container with bridge networking (OrbStack, Docker Desktop), the OAuth callback may fail. Claude Code starts a temporary HTTP server on a random ephemeral port to receive the callback, but that port isn't forwarded to the host browser.

Workaround: Use claude setup-token to authenticate manually, or authenticate on the host first. aibox bind-mounts Claude's .claude/, .claude.json, and XDG cache/config/state locations, so credentials survive container rebuilds.

Upstream tracking

This is tracked at anthropics/claude-code#14528. A fix on the Claude Code side (e.g., configurable callback port) would resolve this properly without compromising container network isolation.

The .asoundrc File

The seeded home configuration includes an .asoundrc file at /home/aibox/.asoundrc. This configures ALSA to route through PulseAudio, so applications that use ALSA rather than PulseAudio directly also get audio output when the audio-voice addon is installed.

Troubleshooting

No sound output

  1. Verify PulseAudio is running on the host:

    pulseaudio --check && echo "running" || echo "not running"
  2. Verify the TCP module is loaded:

    pactl list modules | grep module-native-protocol-tcp
  3. Test from inside the container:

    paplay /usr/share/sounds/freedesktop/stereo/bell.oga

    If the file does not exist, use sox to generate a test tone:

    play -n synth 0.5 sine 440

Connection refused

The PULSE_SERVER address is not reachable from the container.

  • Check that the PulseAudio TCP module is listening on the correct port
  • Check that host.docker.internal resolves from inside the container:
    # From inside the container
    getent hosts host.docker.internal
  • Try using the host's explicit IP address instead

Audio works but is choppy

This is usually a network or resource issue. PulseAudio over TCP adds latency. Ensure the container has sufficient CPU resources and the host is not under heavy load.

Disabling audio

If you do not need audio, set enabled = false in aibox.toml:

[audio]
enabled = false

This removes the PULSE_SERVER environment variable from the container. The audio packages (sox, pulseaudio-utils) remain installed in the base image but are inert without a server to connect to.