Self-hosted high-resolution music streaming. Browse your collection from any browser, sync playback across every tab and device in real time, and transfer playback to MPD speakers around the house — like a personal Spotify running on your own hardware.
Warning
Audiotheque is built with heavy AI assistance (Claude Code). The code is human-reviewed and runs on its author's hardware, but it is a hobby project and there will be rough edges. Don't run it anywhere uptime matters. Bug reports and PRs are welcome.
- Stream your own music to any browser — FLAC, ALAC, MP3, OGG, hi-res.
- Multi-device sync, Spotify-style. Open the app in five tabs; pause in one and the rest pause too.
- Transfer playback to MPD speakers around the house, with auto-discovery via mDNS.
- Self-hosted. Your library, your network, your data.
Library view, dark theme. Playback footer shows the current track streaming to the "Wohnzimmer" MPD speaker — the seek position ticks live as the server polls MPD and pushes state over WebSocket to every connected tab.
# Edit docker-compose.yaml to point at your music folder, then:
docker compose up -dThe app comes up on http://localhost:8080. First-time visit opens a setup
wizard to create your admin account.
See Install below for the full configuration surface.
| Works | Mostly works | Known rough edges |
|---|---|---|
| FLAC / ALAC / MP3 / OGG playback | MPD multi-room | No mobile apps (web only) |
| Library scan with cover art | mDNS device discovery | No playlists or favourites yet |
| Multi-tab/device sync | Hi-res passthrough | Queue ops are REST, not WS |
| First-run setup wizard | Stream-to-MPD URLs | Single SQLite connection — fine for personal scale, not multi-tenant |
| HttpOnly cookie auth, Argon2id hashing | Offline sync planned but not built |
This is a single-household, single-author hobby project. It's not trying to replace Navidrome, Jellyfin, or Plex, and it isn't going to grow a plugin ecosystem.
Most self-hosted music servers either treat playback as a per-device thing (open the app on the phone, it's a different "session" from the browser) or go all-in on classical-library metadata. Audiotheque is the simple thing in the middle: one logical playback session per user, synced over WebSocket, transferable to any tab or MPD device in one click. That's it. That's the pitch.
- Backend — Go, SQLite (
modernc.org/sqlite, pure Go — no cgo), embedded SvelteKit static bundle. - Frontend — SvelteKit with Paraglide i18n, UnoCSS (Tailwind-compatible utility classes via
presetUno). - Audio — FFmpeg for transcoding, file metadata via
taglibshell-outs. - Discovery — mDNS for MPD speakers on the LAN.
- Auth — HttpOnly cookies + Argon2id; signed short-TTL JWTs for MPD stream URLs.
- CI/CD — Dave Farley-style pipeline: commit → image → acceptance → promote.
git clone https://github.com/klabast/audiotheque.git
cd audiotheque
# Edit docker-compose.yaml: set the bind mount for your music folder
# and (optionally) your MPD hosts in environment.
docker compose up -dPrerequisites: Go 1.26+, Node 20+, FFmpeg.
# Backend
cd server
go build -o audiotheque ./cmd/server
# Frontend (embedded into the binary via go:embed)
cd ../ui
npm ci && npm run build
# Run
./server/audiothequeAll configuration is via environment variables. Sensible defaults make most of them optional.
| Variable | Purpose | Default |
|---|---|---|
AUDIOD_PORT |
HTTP port | 8080 |
AUDIOD_DATA_DIR |
DB, cache, reset codes, transcode cache | ./data |
AUDIOD_WEB_DIR |
Override the embedded SvelteKit static bundle (mostly for development) | embedded |
AUDIOD_ALLOWED_ORIGINS |
CORS / WebSocket origin allowlist (comma-separated) | same-origin only |
JWT_SECRET |
Signing secret for auth cookies + stream URLs | auto-generated at startup |
Env var names use the audiod internal-daemon prefix (like mpd,
pulseaudio) — decoupled from the user-facing brand so renames don't
break existing installs.
MPD hosts and mDNS discovery are configured in the UI (Settings → Devices), not via environment variables — they live in the database alongside other user settings.
First-time setup: open the URL, create an admin account, point it at your music library in Settings, trigger a scan.
See CONTRIBUTING.md for the dev workflow, test layout,
and code style. Quick start:
# Backend hot-reload
cd server && go run ./cmd/server
# Frontend dev server (proxies API calls to backend)
cd ui && npm run devBackend tests:
cd server && go test ./...Frontend tests:
cd ui && npm test # vitest unit tests
cd e2e && npm test # cucumber + playwright E2EThe full CI/CD pipeline is in .github/workflows/ci.yml.
The honest version: this is built around what the author personally wants. PRs welcome for anything below, but no commitment to ship.
- HttpOnly cookie auth, Argon2id hashing
- WebSocket real-time playback sync across tabs/devices
- MPD integration with mDNS auto-discovery
- Spotify-style "transfer playback" UX
- Hi-res passthrough (FLAC/ALAC native)
- Queue operations over WebSocket (currently REST)
- Playlists and favourites
- Mobile apps (iOS/Android)
- Offline sync
- Chromecast / DLNA / UPnP
Built on the shoulders of:
A lot of the code was written in collaboration with Claude Code (Anthropic). Every change is human-reviewed, but the volume of code shipped per hour would not have been possible solo. Bugs are mine, not theirs.
GNU Affero General Public License v3.0 (AGPL-3.0-or-later).
Plain English: you can use, modify, fork, and self-host this freely. If you run a modified version as a network service, you must make your source available to your users under the same license. That's the deliberate choice — it lets the project stay open without giving any cloud provider a free path to closed-source commercialization.
