Configuration
The odio installer generates ~/.config/odio-api/config.yaml automatically. The configuration shown below documents all available options — you only need to edit the file if you want to customize the defaults.
For standalone installations, the config file can also be specified with --config <path>.
Minimal config
Section titled “Minimal config”bind: lologLevel: info
api: enabled: true port: 8018 ui: enabled: true cors: origins: ["https://pwa.odio.love", "https://odio-pwa.vercel.app"]API server
Section titled “API server”The api block configures the HTTP server itself: port, embedded UI, SSE stream, and browser CORS. Backends are configured separately, below.
api: enabled: true port: 8018 ui: enabled: true sse: enabled: true cors: origins: - https://pwa.odio.love - https://odio-pwa.vercel.app| Key | Default | Notes |
|---|---|---|
enabled | true | Disable to stop the server entirely. |
port | 8018 | TCP port. Combined with bind to form the listen addresses. |
ui.enabled | true | Serves the embedded web UI at /ui. Auto-disabled at startup if bind does not include lo (the UI is loopback-only by design). |
sse.enabled | true | Serves the SSE event stream at /events. The embedded UI and the PWA both rely on it for real-time updates. |
cors.origins | PWA defaults | Browser origins allowed to call the API, see below. |
The default cors.origins covers the two hosted PWA addresses:
https://pwa.odio.lovehttps://odio-pwa.vercel.app
Network binding
Section titled “Network binding”bind controls which network interfaces the API listens on.
bind: lo # loopback only (default)# bind: enp2s0 # single LAN interface# bind: [lo, enp2s0] # loopback + LAN# bind: [lo, enp2s0, wlan0] # loopback + ethernet + wifi# bind: all # all interfaces (Docker, remote access)Drop-in overrides
Section titled “Drop-in overrides”Since odio-api v0.12.0, ~/.config/odio-api/conf.d/ is loaded automatically: any *.yml file in that directory is merged on top of config.yaml at startup, so you can layer overrides on top of a base config without editing it. Within the systemd block, the system and user arrays are replaced wholesale by an override, not merged item by item, so the override must list every entry you want exposed.
On odio streamer installs, the installer creates this directory and drops a 10-systemd.yml.default reference matching the host's installed services, regenerated on every odio-upgrade run. The .default extension keeps it ignored at runtime. To use it as a starting point, copy it under a .yml name and edit:
cd ~/.config/odio-api/conf.dcp 10-systemd.yml.default 50-my-systemd.yml$EDITOR 50-my-systemd.ymlsystemctl --user restart odio-apiBackend configuration
Section titled “Backend configuration”Each backend can be enabled or disabled independently. Disabling a backend removes all its routes from the API.
Systemd (opt-in, whitelist required)
Section titled “Systemd (opt-in, whitelist required)”See Systemd for endpoint details.
systemd: enabled: true timeout: 90s system: - name: bluetooth.service user: - name: mpd.service url: ":8080" - name: shairport-sync.service - name: snapclient.service url: "http://<snapserver>:1780" - name: spotifyd.service - name: upmpdcli.serviceSince odio-api v0.12.0, each whitelist entry is a map with a name and an optional url. The URL is exposed on the /services response so clients (the embedded UI, PWA, Home Assistant) can render a direct link, which is how myMPD shows up alongside mpd.service on installs that ship it.
Bluetooth
Section titled “Bluetooth”See Bluetooth for endpoint details.
bluetooth: enabled: true timeout: 5s pairingTimeout: 60s idleTimeout: 30m scanTimeout: 60sSystem setup
Section titled “System setup”Since odio doesn't run as root, a few system configuration steps are required.
The user running odio must belong to the bluetooth group:
sudo usermod -a -G bluetooth <username>PulseAudio or PipeWire must be configured to handle Bluetooth audio:
# PulseAudiosudo apt install pulseaudio-module-bluetooth
# PipeWiresudo apt install libspa-0.2-bluetoothBlueZ configuration
Section titled “BlueZ configuration”Edit /etc/bluetooth/main.conf to identify the node as an audio receiver:
[General]Name = odioClass = 0x240428
[Policy]AutoEnable=falseClass of Device (0x240428) breakdown:
0x24— Major Device Class: Audio/Video0x0428— Minor + services: Audio Sink, Loudspeaker, Rendering device
This makes the node appear as a standard Bluetooth speaker. Phones and computers will show a headphone icon and route media audio to it.
AutoEnable=false keeps the adapter off at boot — power is managed by the API (or Home Assistant).
After modifying the configuration, restart the Bluetooth service:
sudo systemctl restart bluetoothmpris-proxy
Section titled “mpris-proxy”BlueZ provides mpris-proxy, a systemd user service that creates an MPRIS player for each connected Bluetooth device. This is what allows the odio API to discover and control Bluetooth playback alongside all other sources.
systemctl --user status mpris-proxy.servicePower management
Section titled “Power management”See Power for endpoint details.
power: enabled: true capabilities: poweroff: true reboot: truePolkit rule
Section titled “Polkit rule”On headless systems, logind requires a polkit rule to allow the odio user to reboot and power off without a graphical session:
/etc/polkit-1/rules.d/10-allow-shutdown.rulespolkit.addRule(function(action, subject) { if ((action.id == "org.freedesktop.login1.power-off" || action.id == "org.freedesktop.login1.power-off-multiple-sessions" || action.id == "org.freedesktop.login1.reboot" || action.id == "org.freedesktop.login1.reboot-multiple-sessions") && subject.user == "odio") { return polkit.Result.YES; }});On desktop systems with a graphical session, this rule is not needed — logind already grants these permissions.
Zeroconf / mDNS
Section titled “Zeroconf / mDNS”See Zeroconf for details. Requires bind to be set to a real network interface.
bind: enp2s0zeroconf: enabled: true