One small, static binary per device — pure Go, no runtime, no dependencies. Each archive is the build attached to the matching tagged release: verify its SHA-256, unpack it, and run ./ondaire. Prefer containers? Pull the master image with Spotify Connect built in.
Tip: For the tightest sync, use the same TYPE of player throughout — e.g. all Raspberry Pi nodes, or all ESP32 nodes. Mixed fleets work (the master equalizes each node's output latency), but identical hardware shares the same latency and clock behaviour, so it lines up best.
ESP32 players
DIY
Turn a PSRAM-equipped ESP32 + an I2S DAC into a real ondaire player: it shows up in the cluster, joins any group, and plays in lock-step like every other room — flashed straight from your browser, no toolchain. Tested on the ESP32-S3 Super Mini and the Waveshare ESP32-S3-Zero (both PSRAM) with a PCM5102A DAC.
The quickest way onto a Linux box: a guided one-liner. It detects your OS and CPU, downloads the matching ondaire build, then walks you through the optional extras — Spotify Connect (go-librespot) and a boot-time systemd service — and sets them up. Installs into /usr/local/lib/ondaire; re-run any time to update.
#!/usr/bin/env bash
# curl -fsSL https://ondaire.rand0m.me/get.sh | sudo bash
set -euo pipefail
# ── 1. Pre-flight ──────────────────────────────────────────────
# Must run as root, on Linux, with tar present.
[ "$(id -u)" = 0 ] || err "run with sudo"
# ── 2. Detect the CPU and pick the matching 64-bit build ───────
case "$(uname -m)" in
x86_64|amd64) ARCH=amd64 ;;
aarch64|arm64) ARCH=arm64 ;; # Raspberry Pi OS 64-bit
*) err "unsupported arch (32-bit is no longer supported)" ;;
esac
# ── 3. Download + install ──────────────────────────────────────
# Binary lands in /usr/local/lib/ondaire, symlinked onto your PATH.
fetch "$BASE/assets/downloads/ondaire-linux-$ARCH.tar.gz" | tar -xz
install -m755 ondaire /usr/local/lib/ondaire/ondaire
ln -sf /usr/local/lib/ondaire/ondaire /usr/local/bin/ondaire
# ── 4. Choose a role (prompts read the terminal, so they work
# even under curl | bash) ─────────────────────────────────
if ask "Run the web UI on this node?"; then
ROLE="master,playback" # serves the UI, gossips, can play audio
else
ROLE="playback" # receive-only, driven by a master
fi
# ── 5. Spotify Connect — masters only, optional ────────────────
# Fetches the matching go-librespot build alongside ondaire.
[ "$ROLE" != playback ] && ask "Install Spotify Connect?" && install_go_librespot
# ── 6. Boot service — optional ─────────────────────────────────
# Writes /etc/systemd/system/ondaire.service and enables it, so
# ondaire starts at boot and restarts on failure.
ask "Start ondaire at boot (systemd)?" && install_systemd_unit
# ── 7. Appliance hardening — optional, for an SD-card Pi ───────
# Console-only (frees the audio card from the desktop), trims extra
# services, sends logs to RAM, disables swap.
ask "Harden as a headless audio appliance?" && harden_appliance
echo "ready — open the web UI at http://<this-host>:8080"
Raspberry Pi OS Lite (64-bit) on a Pi 3 / 4 / 5 or Zero 2, or any other arm64 Linux. 32-bit Pi OS is no longer supported.
linux · arm64
Heads-up: Opus playback loads libopus at runtime. The Desktop image already has it; on a minimal Lite install that lacks it, add it with sudo apt install libopus0 (uncompressed PCM works without it). Audio hardware that needs third-party drivers is out of ondaire's scope — get the card working in Linux first.
For a NAS or server: runs a master — mount your music library and serve it to your players, with Spotify Connect (go-librespot) built in. Multi-arch: amd64 · arm64.
container image
The image runs the master role only by default — it sources audio (your library + Spotify Connect) and controls the cluster, but does not play locally. Reaching host audio hardware (sound cards, USB DACs, I2S) from a container isn't reliable and is out of scope: the actual output happens on your players (the Pi binary, or an ESP32 node). Mount your library read-only on /media; mutable state lives on /data. You can change any default with the ONDAIRE_* environment variables — see the config flags below.
--network host is required. Players discover the master over mDNS and go-librespot advertises Spotify Connect over zeroconf — multicast doesn't cross Docker's bridge network (and ports bind-or-increment, so there's nothing to publish).
Almost every flag has an ONDAIRE_* environment equivalent (handy in Docker); all are optional, with sensible defaults. Ports left at their default bind-or-increment (run several nodes on one box); a port you set explicitly is pinned (binds exactly or exits).
param
env var
default
description
--name <name>
—
node id
display name + Spotify device name (first start only)