Files
audioserver/README.md
T
fegger 8247692aa2 - Dependency Updates & Cleanup
- Frontend Security Enhancements (Nginx/Entrypoint)
- Docker Networking & Binding
- Code Bug Fix
2026-05-19 10:11:15 +02:00

17 KiB

audioserver

audioserver is a Docker Compose based home audio stack for a Raspberry Pi audio server. It combines Mopidy, Snapserver, Icecast, a React control UI, Bandcamp/Spotify credential helpers, and optional native iOS/Android clients.

The intended deployment target is an always-on Linux host on the same LAN as Snapcast clients and a HiFiBerry BeoCreate DSP device.

What This Stack Provides

  • Mopidy music server for local files, Spotify, YouTube, Beets, Iris, MPD, and HTTP JSON-RPC control.
  • Snapserver multi-room audio with streams for Mopidy, turntable input, and several TCP sources.
  • Icecast FLAC stream for browser and mobile playback outside Snapcast.
  • audiocontrol web UI served by Nginx for Mopidy, Snapcast, and BeoCreate DSP control.
  • Nginx API proxy so the browser talks to one origin instead of many backend services.
  • Turntable capture helper that records from a HiFiBerry ALSA device on the host and writes PCM into Snapserver.
  • Now-playing helper service for Snapcast metadata and audio recognition.
  • GrooveAudio mobile apps for iOS and Android streaming/control.

Architecture

LAN / browser / mobile app
        |
        v
  frontend container, Nginx :${FRONTEND_PORT:-8180}
        |-- React audiocontrol UI
        |-- /api/mopidy/*       -> mopidy:6680
        |-- /ws/mopidy          -> mopidy:6680
        |-- /api/snapcast/rpc   -> snapserver:1780/jsonrpc
        |-- /ws/snapcast        -> snapserver:1780/jsonrpc
        |-- /api/hifiberry/*    -> BeoCreate DSP REST API
        |-- /api/bandcamp/*     -> bandcamp-api:8091
        |-- /api/spotify/*      -> bandcamp-api:8091
        |-- /api/now-playing/*  -> host now-playing service :8090
        |-- /stream.flac        -> icecast:8000/stream.flac

  mopidy container
        |-- writes 48 kHz / 16-bit / stereo PCM to /audio/mopidy.fifo
        |-- mirrors PCM to /audio/cava.fifo and /audio/stream.fifo

  stream-relay container
        |-- reads /audio/stream.fifo
        |-- encodes FLAC with ffmpeg
        |-- publishes to icecast:8000/stream.flac

  snapserver container
        |-- reads /audio/mopidy.fifo
        |-- reads /audio/turntable.fifo
        |-- connects to configured TCP audio sources
        |-- serves Snapcast clients on port 1704

  host turntable-capture systemd service
        |-- records from HiFiBerry ALSA capture device
        |-- writes raw PCM to /audio/turntable.fifo

Repository Layout

.
|-- docker-compose.yml              # Main service stack
|-- .env.example                    # Environment template
|-- frontend/                       # Nginx image and proxy template
|-- audiocontrol/                   # React/Vite control UI
|-- mopidy/                         # Mopidy image and config
|-- snapserver/                     # Snapserver image and config
|-- icecast/                        # Icecast image and config template
|-- stream-relay/                   # FIFO-to-Icecast FLAC relay
|-- bandcamp-api/                   # Credential helper microservice
|-- scripts/                        # Host helper scripts and systemd units
|-- GrooveAudio (Android)/          # Native Android client
|-- GrooveAudio (iOS)/              # Native iOS client
|-- MOBILE_APP_INSTRUCTIONS.md      # Extra mobile/networking notes
`-- IMPLEMENTATION_SUMMARY.md       # Historical implementation notes

Prerequisites

Host

  • Linux host, preferably Raspberry Pi OS or Debian/Ubuntu.
  • Docker Engine and Docker Compose plugin.
  • git, curl, alsa-utils, and systemd for host helper services.
  • A reachable BeoCreate/HiFiBerry DSP API if you want DSP control from the UI.
  • A capture-capable ALSA device if you want turntable input.

Install the common packages on Debian/Raspberry Pi OS:

sudo apt update
sudo apt install -y docker.io docker-compose-plugin git curl alsa-utils
sudo usermod -aG docker "$USER"

Log out and back in after adding your user to the docker group.

Optional Host Packages

The now-playing helper can use Flask, NumPy, CAVA, and SongRec:

sudo apt install -y python3 python3-pip cava
pip3 install -r scripts/requirements.txt

SongRec CLI is optional and must be installed separately if you want acoustic recognition through snapcast-now-playing.py.

Development Tools

Only needed when developing outside Docker:

  • Node.js 20+ for audiocontrol.
  • Android Studio for the Android client.
  • Xcode for the iOS client.

First-Time Setup

1. Clone The Repository

Choose a stable location. The systemd units in scripts/ expect a fixed path, so avoid temporary directories.

git clone <repo-url> /home/admin/appdata/audioserver
cd /home/admin/appdata/audioserver

Any path is fine, but if you use a different one, update the WorkingDirectory, ExecStart, and EnvironmentFile paths in the systemd service files before installing them.

2. Create Your Environment File

cp .env.example .env
nano .env

At minimum, review these values:

BEOCREATE_HOST=beocreate.local
BEOCREATE_PORT=13141
AUDIO_PIPES_DIR=/opt/audiocontrol/pipes
MUSIC_DIR=/opt/audiocontrol/music
FRONTEND_PORT=8180
SNAPSERVER_HOST=192.168.178.100
HIFIBERRY_CARD=sndrpihifiberry
ICECAST_LISTENER_PASSWORD=groove_listen

Set AUDIOCONTROL_PASSWORD if the web UI should require HTTP Basic Auth:

AUDIOCONTROL_USERNAME=admin
AUDIOCONTROL_PASSWORD=change-me

Leave AUDIOCONTROL_PASSWORD empty to disable Basic Auth.

3. Create Host Directories

Create the default music and FIFO directories:

sudo mkdir -p /opt/audiocontrol/music
sudo bash scripts/init-pipes.sh

If you use a non-default FIFO path, pass it to the script and keep .env aligned:

sudo bash scripts/init-pipes.sh /srv/audioserver/pipes

The script creates:

  • mopidy.fifo
  • turntable.fifo
  • cava.fifo
  • stream.fifo

4. Add Music

Put local music in the path configured by MUSIC_DIR. By default:

sudo mkdir -p /opt/audiocontrol/music
sudo rsync -av /path/to/music/ /opt/audiocontrol/music/

The directory is mounted read-only into the Mopidy container at /music.

5. Build And Start The Stack

docker compose up -d --build
docker compose ps

Watch startup logs:

docker compose logs -f

Open the web UI:

http://<audio-server-host>:8180

Use the port from FRONTEND_PORT if you changed it.

6. Scan The Mopidy Local Library

After adding files to MUSIC_DIR, scan the library:

docker compose exec mopidy mopidy local scan

Restart Mopidy if needed:

docker compose restart mopidy

7. Install Turntable Capture

Turntable capture runs on the host, not in Docker, because it needs ALSA device access.

First check the capture card name:

arecord -l

Set HIFIBERRY_CARD in .env to the matching ALSA card name. Then install the service:

sudo cp scripts/turntable-capture.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now turntable-capture

Check logs:

journalctl -u turntable-capture -f

If the repo is not located at /home/admin/appdata/audioserver, edit /etc/systemd/system/turntable-capture.service before enabling it.

8. Optional: Install Now-Playing Service

The frontend proxies /api/now-playing/ to a host service on port 8090.

Install Python dependencies:

pip3 install -r scripts/requirements.txt

Install the service:

sudo cp scripts/snapcast-now-playing.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now snapcast-now-playing

Check logs:

journalctl -u snapcast-now-playing -f

As with the turntable service, update paths in the unit file if the repo lives somewhere other than /home/admin/appdata/audioserver.

Service Ports

Port Service Bind Purpose
8180 frontend configurable via FRONTEND_PORT Web UI, API proxy, /stream.flac proxy
1704 Snapserver 0.0.0.0 Snapcast client audio stream
1705 Snapserver TCP RPC RPC_BIND, default 127.0.0.1 Direct Snapcast JSON-RPC TCP
1780 Snapserver HTTP RPC RPC_BIND, default 127.0.0.1 Direct Snapcast HTTP/WebSocket API
8000 Icecast STREAM_BIND, default 0.0.0.0 Direct FLAC stream server
6600 Mopidy MPD Docker network only by default MPD access inside stack
6680 Mopidy HTTP Docker network only by default Proxied through frontend
8090 now-playing helper host service Proxied through frontend

Mopidy HTTP is intentionally not exposed on the host in docker-compose.yml. Use the frontend proxy for browser and mobile control.

Important URLs

Replace <host> with your audio server hostname or IP.

Web UI:                  http://<host>:8180/
Mopidy JSON-RPC proxy:   http://<host>:8180/api/mopidy/rpc
Mopidy WebSocket proxy:  ws://<host>:8180/ws/mopidy
Snapcast RPC proxy:      http://<host>:8180/api/snapcast/rpc
Snapcast WebSocket:      ws://<host>:8180/ws/snapcast
Icecast direct stream:   http://<host>:8000/stream.flac
Proxied stream:          http://<host>:8180/stream.flac

Environment Reference

Variable Default Description
BEOCREATE_HOST beocreate.local Hostname/IP for the BeoCreate DSP REST API.
BEOCREATE_PORT 13141 BeoCreate DSP REST API port.
AUDIO_PIPES_DIR /opt/audiocontrol/pipes Host directory containing audio FIFOs.
MUSIC_DIR /opt/audiocontrol/music Host music library mounted into Mopidy.
FRONTEND_PORT 8180 Host port for the Nginx/React frontend.
AUDIOCONTROL_USERNAME admin Basic Auth username when auth is enabled.
AUDIOCONTROL_PASSWORD empty Basic Auth password. Empty disables auth.
RPC_BIND 127.0.0.1 Host bind address for direct Snapserver RPC ports.
STREAM_BIND 0.0.0.0 Host bind address for Icecast port 8000.
TZ Europe/Vienna Container timezone.
HIFIBERRY_CARD sndrpihifiberry ALSA capture card name for turntable input.
ICECAST_SOURCE_PASSWORD groove_source Source password used by stream-relay.
ICECAST_RELAY_PASSWORD groove_relay Icecast relay password.
ICECAST_ADMIN_PASSWORD groove_admin Icecast admin password.
ICECAST_LISTENER_PASSWORD groove_listen Listener password used by mobile apps.
SPOTIFY_CLIENT_ID empty Spotify application client ID for Mopidy Spotify.
SPOTIFY_CLIENT_SECRET empty Spotify application client secret.
SNAPSERVER_HOST 192.168.178.100 LAN address of this audio server, used by Snapserver/Iris.
BEETS_HOST 192.168.178.100 Host running Beets web service.
TCP_POP_HOST 192.168.178.95 TCP source host for Snapserver stream pop.
TCP_TP_HOST 192.168.178.89 TCP source host for Snapserver stream tp.
TCP_FRAMEWORK_HOST 192.168.178.183 TCP source host for Snapserver stream framework.

Operating The Stack

Start or restart everything:

docker compose up -d --build

Stop everything:

docker compose down

Show service status:

docker compose ps

Follow all logs:

docker compose logs -f

Follow one service:

docker compose logs -f mopidy
docker compose logs -f snapserver
docker compose logs -f frontend
docker compose logs -f icecast

Restart one service:

docker compose restart mopidy
docker compose restart snapserver
docker compose restart frontend

Open a shell in a container:

docker compose exec mopidy bash
docker compose exec frontend sh

Rebuild after config or source changes:

docker compose build
docker compose up -d

Updating

git pull
docker compose pull
docker compose up -d --build
docker compose ps

If .env.example changed, compare it with your .env and copy over any new variables you need.

Audio Routing

Mopidy To Snapcast

Mopidy writes raw PCM into /audio/mopidy.fifo. Snapserver reads that FIFO as the mopidy stream:

Mopidy -> /audio/mopidy.fifo -> Snapserver stream "mopidy" -> Snapcast clients

Mopidy also mirrors the same audio into:

  • /audio/cava.fifo for visualization.
  • /audio/stream.fifo for Icecast streaming.

Mopidy To Icecast

The stream-relay container reads /audio/stream.fifo, encodes FLAC with ffmpeg, and publishes to Icecast:

Mopidy -> /audio/stream.fifo -> stream-relay -> Icecast /stream.flac

The stream is available directly on port 8000 and through the frontend proxy.

Turntable To Snapcast

The host turntable-capture service records from ALSA and writes raw PCM to the turntable FIFO:

Turntable / HiFiBerry capture -> turntable-capture -> /audio/turntable.fifo
    -> Snapserver stream "turntable" -> Snapcast clients

All primary PCM paths use 48 kHz, 16-bit, stereo audio.

Web UI Development

The Docker stack builds the React app automatically from audiocontrol/.

For local frontend development:

cd audiocontrol
npm install
npm run dev

The Vite development server runs on:

http://localhost:5173

Build manually:

npm run build

Preview the production build:

npm run preview

Mobile Apps

The native clients live in:

  • GrooveAudio (Android)/
  • GrooveAudio (iOS)/

They are designed to use:

  • Port 8000 for the Icecast FLAC stream.
  • Port 8180 for proxied Mopidy and Snapcast control APIs.
  • ICECAST_LISTENER_PASSWORD from .env for stream authentication.

Default app settings assume:

Host:     192.168.178.100
Password: groove_listen

Change those in the app settings if your server IP or listener password differs. See MOBILE_APP_INSTRUCTIONS.md for additional notes about mobile and remote access.

Android

cd "GrooveAudio (Android)"
./gradlew assembleDebug

Or open the folder in Android Studio and run the app on a device.

iOS

Open GrooveAudio (iOS)/GrooveAudio.xcodeproj in Xcode, select a device or simulator, and run the app.

Spotify And Bandcamp

Spotify

  1. Create a Spotify application in the Spotify Developer Dashboard.

  2. Put the credentials in .env:

    SPOTIFY_CLIENT_ID=...
    SPOTIFY_CLIENT_SECRET=...
    
  3. Recreate Mopidy:

    docker compose up -d --build mopidy
    

The frontend also includes a proxied credential store under /api/spotify/ served by bandcamp-api.

Bandcamp

The bandcamp-api service stores Bandcamp credentials or cookies in the bandcamp_config Docker volume. The frontend proxies it under /api/bandcamp/.

Security Notes

  • Set AUDIOCONTROL_PASSWORD before exposing the UI outside a trusted LAN.
  • Keep RPC_BIND=127.0.0.1 unless direct Snapserver RPC access is required.
  • Prefer the frontend proxy for control APIs instead of exposing backend ports.
  • Icecast binds to 0.0.0.0 by default through STREAM_BIND; restrict it if you do not want direct LAN access to port 8000.
  • Do not commit .env; it may contain passwords and API credentials.

Troubleshooting

Frontend Does Not Load

Check the frontend container and logs:

docker compose ps frontend
docker compose logs -f frontend

Verify the configured port:

docker compose port frontend 80

Basic Auth Fails

Check AUDIOCONTROL_USERNAME and AUDIOCONTROL_PASSWORD in .env, then recreate the frontend:

docker compose up -d --force-recreate frontend

BeoCreate DSP Is Unreachable

Confirm the host and port in .env:

set -a
. ./.env
set +a
curl "http://$BEOCREATE_HOST:$BEOCREATE_PORT/api/volume"

If that fails from the host, fix DNS/IP routing before debugging Docker.

No Mopidy Audio In Snapcast

Confirm the FIFOs exist:

ls -l "${AUDIO_PIPES_DIR:-/opt/audiocontrol/pipes}"

Then check service logs:

docker compose logs -f mopidy snapserver

If Mopidy started before Snapserver was ready, restart Mopidy:

docker compose restart mopidy

Local Music Is Missing

Confirm MUSIC_DIR contains music files and rescan:

docker compose exec mopidy mopidy local scan
docker compose restart mopidy

No Turntable Audio

Check ALSA devices:

arecord -l

Update HIFIBERRY_CARD in .env, then restart the service:

sudo systemctl restart turntable-capture
journalctl -u turntable-capture -f

Icecast Stream Is Silent Or Missing

Check Icecast and relay logs:

docker compose logs -f icecast stream-relay

Verify the stream URL:

curl -I http://localhost:8000/stream.flac
curl -I http://localhost:8180/stream.flac

The stream only carries Mopidy audio. If Mopidy is stopped or idle, the stream may be silent.

Snapcast Clients Cannot Connect

Snapcast clients need access to port 1704 on the audio server:

sudo ufw allow 1704/tcp

Confirm Snapserver is listening:

docker compose logs -f snapserver

Now-Playing API Returns Errors

Check the host service:

systemctl status snapcast-now-playing
journalctl -u snapcast-now-playing -f

Make sure the service unit paths match the actual repository path and that Python dependencies from scripts/requirements.txt are installed.

Backup Notes

Important persistent state lives in:

  • .env
  • MUSIC_DIR
  • Docker volume audioserver_mopidy_data
  • Docker volume audioserver_bandcamp_config
  • Any systemd unit overrides you create

List volumes:

docker volume ls | grep audioserver

Back up named volumes with your normal Docker volume backup process.