Skip to main content

Audio Support

The aibox base image includes audio support for Claude Code's voice features. Audio is bridged from the container to the host via PulseAudio over TCP.

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
pulse_server = "tcp:host.docker.internal:4714"
FieldDefaultDescription
enabledfalseWhether to set up audio environment variables in the container
pulse_servertcp:host.docker.internal:4714PulseAudio server address

When enabled = true, the generated docker-compose.yml sets PULSE_SERVER in the container environment.

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 audio check

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

aibox audio setup 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 audio check 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 — the .claude directory is bind-mounted into the container, so credentials are shared.

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 base image 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.

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.