rmpd is a modern, high-performance, memory-safe music server written in pure Rust. It aims for 100% compatibility with the Music Player Daemon (MPD) protocol while providing first-class extensibility through a plugin architecture.
- 🎵 MPD Protocol Compatible — works with existing MPD clients (ncmpcpp, mpc, Cantata, rmpc)
- 🦀 Pure Rust — memory-safe; even APE and WavPack decode without C bindings
- 🔌 Extensible — compile-time plugin registries for outputs, decoders, and music sources
- 🎧 High-Quality Audio — DSD (DoP + PCM fallback), ReplayGain, gapless playback, crossfade
- 🎼 Format Support — FLAC, MP3, Ogg Vorbis, WAV, AAC, ALAC, APE, WavPack, DSD and more; see Format Support
- 🏠 Multi-Room — HTTP streaming and Snapcast; see Integrations
- 🖥️ Desktop Integration — MPRIS D-Bus and mDNS auto-discovery; see Integrations
- ⚙️ systemd Native —
Type=notifyreadiness and socket activation, like MPD; see systemd - 🌐 Remote Libraries — OpenSubsonic servers as a music source; see Integrations
rmpd/
├── rmpd/ # CLI entry point / main binary
├── rmpd-core/ # Config, error, event bus, queue, song/tag, state — shared by every crate
├── rmpd-macros/ # #[derive(CommandMetadata)] proc macro for MPD command dispatch
├── rmpd-protocol/ # MPD wire protocol: parser, command dispatch, connection/server, MPRIS
├── rmpd-player/ # Audio engine: symphonia decoding, DSD/DoP, outputs, resampling
├── rmpd-library/ # Filesystem scanner, SQLite database, tag/artwork extraction, search
├── rmpd-plugin/ # Cross-cutting plugin SPI (currently the MusicSource trait)
├── rmpd-source/ # Music-source registry + backends (filesystem, OpenSubsonic)
└── rmpd-stream/ # HTTP(S) streaming input source for internet radio (ICY metadata)
Requires Rust 1.85+ (the workspace uses edition 2024).
System dependencies:
# Ubuntu/Debian
sudo apt-get install libasound2-dev pkg-config
# macOS
brew install pkg-configcargo build --release./target/release/rmpd --bind 127.0.0.1 --port 6600 --music-dir ~/Music# Check status
mpc status
# Update library
mpc update
# Add and play music
mpc add /
mpc play
# Play internet radio (any HTTP/HTTPS stream URL)
mpc add https://stream.example/radio.mp3
mpc play
mpc current # shows the live ICY "now playing" title for streamsLibrary scanning/tagging and playback both go through symphonia, but they are not the same list: a file can scan and tag without a decoder to play it.
| Extension(s) | Scan / tag / browse | Playback |
|---|---|---|
flac |
✅ | ✅ |
mp3 |
✅ | ✅ |
ogg, oga |
✅ | ✅ |
opus |
✅ | ❌ — demuxed, no Opus decoder |
wav |
✅ | ✅ |
aiff, aif |
✅ | ✅ |
m4a |
✅ | ✅ |
aac |
✅ | ✅ |
ape (Monkey's Audio) |
✅ | ✅ — pure-Rust decoder |
wv (WavPack) |
✅ | ✅ — pure-Rust decoder |
dsf, dff (DSD) |
✅ | ✅ — see DSD |
mka, webm |
✅ | ✅ |
wave, mp4, alac, caf |
❌ (not scanned into the library) | ✅ — playable if referenced directly |
Musepack (.mpc) is not supported at all: no scan, no tag, no playback.
.dsf/.dff, DSD64 through DSD256 and higher, all scan, tag, and play.- DoP (DSD over PCM) sends a native DSD bitstream to a bit-perfect DAC over a raw ALSA
hw:device. Opt in withaudio.dop = "yes"(or"auto"to use DoP only whenaudio.deviceis set) orRMPD_DOP=1. DSD64, DSD128 and DSD256 are all supported; DoP always needs a PCM rate ofdsd_rate / 16(176.4kHz for DSD64, 705.6kHz for DSD256), so a DAC that cannot accept that rate falls back to PCM automatically. - PCM fallback (the default) decodes DSD to a 44.1kHz-family rate and resamples to the output device's native rate via the configured
resampler_quality, so a sound server such as PipeWire never resamples internally — avoiding underruns and keeping DSD's ultrasonic noise out of the audible band.
rmpd does not require you to create a config file. On first start, if no
config file is found, rmpd writes a commented starter config to
~/.config/rmpd/rmpd.toml and logs the path it chose. You can also manage
this explicitly:
--generate-configwrites the starter config on demand and refuses to overwrite an existing file.--print-config-pathprints the path that would be used, without writing anything.--no-configskips file discovery entirely and runs on built-in defaults.
When --config is not given, rmpd searches these locations in order and
uses the first one that exists:
- the path given to
--config $XDG_CONFIG_HOME/rmpd/rmpd.toml(usually~/.config/rmpd/rmpd.toml)~/.rmpd.toml~/.rmpd/rmpd.toml/etc/rmpd/rmpd.toml
This mirrors MPD's own search order ($XDG_CONFIG_HOME/mpd/mpd.conf,
~/.mpdconf, ~/.mpd/mpd.conf, /etc/mpd.conf).
Every section and key is optional — anything omitted uses a built-in
default, so a partial config is valid. Path values expand a leading ~,
$HOME, and MPD's $XDG_CONFIG_HOME, $XDG_MUSIC_DIR, $XDG_CACHE_HOME,
$XDG_DATA_HOME, $XDG_STATE_HOME and $XDG_RUNTIME_DIR.
The state file defaults to $XDG_STATE_HOME/rmpd/state (usually
~/.local/state/rmpd/state; $STATE_DIRECTORY wins under systemd). An
existing state file at the old default, ~/.config/rmpd/state, keeps being
used until the new one exists.
A minimal config:
[general]
music_directory = "~/Music"
log_level = "info"
[network]
bind_address = "127.0.0.1"
port = 6600
[audio]
default_output = "alsa"
replay_gain = "auto"See rmpd.toml for a complete, annotated configuration example.
Additional [general]/[network] keys (see rmpd.toml for full examples):
state_file_interval— seconds between periodic state-file saves;0disables periodic saving (default 120)log_file— write logs to this file instead of stdout (default: stdout)max_playlist_length— maximum number of songs in the queue (default 16384)save_absolute_paths_in_playlists— store absolute paths in saved.m3uplaylists (default false)metadata_to_use— restrict which tags are read/stored during scans (default: all tags)network.passwords— array ofpassword/permissionspairs granting scoped access (default: none)network.default_permissions/network.local_permissions/network.host_permissions— permission sets for unauthenticated, local-socket, and per-host clients, mirroring MPD's access control (default: unrestricted)network.max_command_list_size/network.max_output_buffer_size— per-client buffer caps in bytes (defaults 16 MiB / 8 MiB)network.zeroconf_name— mDNS/Zeroconf service name,%hexpands to the hostname (defaultrmpd@%h)
Unrecognized keys are reported at startup instead of being silently ignored, with a suggestion when a typo is likely, for example:
unknown config key `general.msic_directory` (did you mean `music_directory`?)
Keys carried over from mpd.conf produce a migration hint pointing at the
rmpd equivalent. Unknown keys only warn — they never stop the daemon.
An explicitly passed --config file that cannot be read or parsed is
fatal, since a config you asked for by name should never be silently
substituted with defaults.
A missing or nonexistent music_directory is not fatal: rmpd warns,
starts anyway, and skips library scanning, matching MPD's
degrade-don't-die behaviour.
log_level under [general] controls logging (trace/debug/info/
warn/error). --verbose forces debug, and the RUST_LOG environment
variable overrides both.
The following keys were parsed in older versions but never did anything and have been removed; a config that still sets them gets a startup warning naming each one:
audio.gapless— gapless playback is always on- the entire
[decoder]section, includingdecoder.enabled— decoders come from a compile-time registry database.cache_sizedatabase.fts_enabled
rmpd exposes a native MPRIS interface on the session D-Bus as org.mpris.MediaPlayer2.rmpd. This lets Linux desktops (GNOME Shell, KDE Plasma), playerctl, lock screens, and multimedia keys discover and control rmpd directly — no external bridge such as mpDris2 required. It is enabled by default and can be toggled with mpris under [network].
playerctl -p rmpd metadata
busctl --user introspect org.mpris.MediaPlayer2.rmpd /org/mpris/MediaPlayer2rmpd also advertises itself over mDNS/Zeroconf so MPD clients on the local network can auto-discover the server.
rmpd speaks systemd's protocols natively, without linking libsystemd:
- Readiness — the units use
Type=notify; rmpd sendsREADY=1once every listener is bound andSTOPPING=1on shutdown. Don't pass--daemonizeunder systemd (rmpd ignores it there). - Socket activation — with
rmpd.socketenabled, systemd owns the TCP and Unix sockets and starts rmpd on the first connection. Like MPD, rmpd then serves the passed sockets and ignoresbind_address,portandunix_socket.
Units live in contrib/systemd: rmpd.service and
rmpd.socket for the user instance, and system/ for a system-wide
instance running as the rmpd user.
install -Dm644 contrib/systemd/rmpd.{service,socket} -t ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now rmpd.service # or: rmpd.socket for on-demand startrmpd can aggregate a remote OpenSubsonic
server (Navidrome, Airsonic, gonic, …) into its library as a music source.
The remote catalog is synced into the database at startup and on update, and
tracks stream on demand through the same HTTP path used for internet radio — so
any MPD client browses and plays them like local files (under mount-style paths
such as home/Artist/Album/<id>.flac, where the source name is the mount point).
Build with the subsonic feature, then add one or more [[source]] blocks:
cargo build --release --features subsonic[[source]]
name = "home" # becomes the mount point (top-level directory)
type = "subsonic"
enabled = true
url = "https://music.example.com"
username = "alice"
password = "secret" # or use `api_key = "..."` instead
# max_bitrate = 320 # optional server-side transcode cap (kbps)
# format = "mp3" # optional transcode target ("raw" = no transcode)Credentials are never written to logs. An unreachable server is skipped at startup without aborting (previously-synced tracks remain browsable).
rmpd plays to all enabled outputs simultaneously, so local audio and a network stream can run at once. Two routes to networked/multi-room playback:
- HTTP streaming — enable a
type = "httpd"output (default port 8000) and point any browser, phone, or another MPD/VLC client athttp://<host>:8000. Works today, no extra daemon required (encoder = "wav"or"pcm"). The same output also serves Shoutcast/Icecast clients (ICY 200 OKgreeting plus interleavedStreamTitlemetadata). - Snapcast (synchronized) — enable a
type = "fifo"output writing to/tmp/snapfifoand run an external Snapcastsnapserverreading that FIFO for sample-accurate multi-room sync.
- Core: MPD protocol server (TCP/Unix sockets), event bus, configuration management, logging via
tracing - Library: filesystem scanning + watcher, SQLite database, metadata/artwork extraction via
symphonia, full-text search viatantivy - MPD protocol: playback commands (play/pause/stop/seek), queue management (add/delete/move/shuffle), database queries (find/search/list), status/statistics, playlist management (
.m3u,.pls, XSPF/ASX;.cuesheets expand into range-restricted virtual tracks, reported withRealUri), output control, stickers on songs, playlists, tags and filters - Audio: gapless playback, crossfade and MixRamp transitions, ReplayGain, internet radio input with Shoutcast/Icecast (ICY) "now playing" metadata — see Format Support for codec coverage and Integrations for multi-room, MPRIS, and OpenSubsonic
- Network storage:
mount/unmountshell out to the systemmount(8)for NFS and SMB/CIFS shares (Linux and macOS), exposed under the music directory like MPD's storage plugins — no in-process NFS/SMB client
- Compressed stream encoders (FLAC / Opus / Vorbis) for the
httpdoutput
- ✅ mpc — command-line client
- ✅ ncmpcpp — TUI client
- ✅ Cantata — Qt GUI client
- ✅ rmpc — modern TUI client
- 🚧 MPDroid — Android client (testing in progress)
- 🚧 MPDluxe — iOS client (testing in progress)
cargo test --workspace --all-features# Format code
cargo fmt
# Run clippy
cargo clippy --workspace --all-targets --all-featuresGitHub Actions runs formatting/clippy/cargo doc checks, a cross-platform test
matrix (Ubuntu + macOS, stable + nightly), a compatibility suite (state
persistence, database compatibility, decoder validation), a dependency-pruning
lint (cargo-machete), coverage reporting (Codecov), and release builds for 3
targets (x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu,
aarch64-apple-darwin — no x86_64 macOS build). A separate workflow runs
cargo-audit/cargo-deny security audits, and Renovate keeps dependencies
current. See CI.md for details.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Run
cargo fmtandcargo clippy - Submit a pull request
See CI.md for development guidelines and CI/CD information.
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
- Inspired by the original Music Player Daemon
- Built with modern Rust audio libraries: Symphonia, cpal
- Special thanks to the Rust audio community
- Documentation: CI.md - CI/CD and development guide
- MPD Protocol: MPD Protocol Documentation
- Issue Tracker: GitHub Issues
- Discussions: GitHub Discussions
