MPD with Nomad

I’d seen MPD before (well, it was ncmpcpp), but I gave it a try when I’d gotten sick of needing to stream every piece of content I wanted to consume. The setup didn’t seem hard, but I also wanted to take a dip into using service schedulers.

  • Setting this up seemed like a solid night project,
  • I’ve been using a lot of HashiCorp tooling in my day to day, so It’s great opportunity to finally learn and toy with Nomad,
  • I was too indifferent to write a .plist file for launchd to auto-start MPD when my Macbook boots (plus launchd isn’t cross-platform),
  • Nomad’s raw_exec driver could be swapped out for an MPD Docker container later (possibly easily!)

Setup

Assuming MPD’s config is set up correctly, the Nomad jobspec is pretty straightforward:

job "mpd" {
  type = "service"

  # run on -dev datacenter
  datacenters = ["dc1"]

  meta {
    # mpd config file location
    MPD_CONFIG = "~/.config/mpd/mpd.conf"
  }

  group "daemon" {
    task "server" {
      # don't isolate the process
      driver = "raw_exec"

      # do the thing
      config {
        # use the mpd binary resolved by $PATH
        command = "mpd"

        # log stderr and don't fork to the background
        args = [
          "--no-daemon",
          "--stderr",
          "${NOMAD_META_MPD_CONFIG}",
        ]
      }
    }
  }
}

Regarding the command args, --no-daemon prevents MPD from auto-forking to the background, and --stderr forces MPD to log event details to Stderr rather than omitting the logging entirely. I hadn’t planned to deploy an MPD service anywhere either, and so using a dev cluster was fine to start.

Assuming there’s a Nomad cluster running on 127.0.0.1:4646 (that way you can use defaults), schedule the MPD Nomad job as you would for any other:

t$ nomad run mpd.nomad
==> Monitoring evaluation "d36f7862"
    Evaluation triggered by job "mpd"
    Allocation "296eced2" created: node "f8146dbe", group "daemon"
    Allocation "296eced2" status changed: "pending" -> "running"
    Evaluation status changed: "pending" -> "complete"
==> Evaluation "d36f7862" finished with status "complete"
t$ mpc status
Christopher Schwarzwalder & David Dorad & Iannis Ritter - Elevator Music (Original Mix)
[playing] #1/1   5:07/7:45 (66%)
volume:100%   repeat: off   random: off   single: off   consume: off
t$ nomad alloc logs --stderr 296
client: [0] opened from 127.0.0.1:51000
client: [1] opened from 127.0.0.1:51010
client: [1] closed

Be sure to use the --stderr flag when looking at the logs: MPD is fairly quiet otherwise. Ncmpcpp auto-connected from another terminal I’d had open, hence the two separate clients. MPD and MPC functioned perfectly, so any legitimate MPD client should be unaffected.

Remote Streaming

If you’ve got a server, the next step is to set up remote access. The MPD entry on the Arch Wiki for configuring HTTP streaming steps in here.

Replace the existing audio_output config (or add it as a new entry) in the mpd.conf:

audio_output {
    type      "httpd"
    name      "mpd-server"
    port      "3456"
    bitrate   "128"
    format    "44100:16:1"
    always_on "yes"
    tags      "yes"
}

…and after stopping and restarting the mpd job MPD should load the new config and initialize the httpd output device. By either hooking up a local MPD daemon to the remote server or opening the server-port combo in browser you can stream to almost anything 👍

The httpd output capability also means that a containerized/Dockerized MPD server could be done exactly the same way without sharing the host’s hardware audio devices, which would be a nightmare for cross-platform support.

Future plans:

  • Containerizing MPD, there’s already a number of existing MPD Docker containers, like vimagick/mpd,
  • Health checks,
  • Templated .mpd.conf