voidcruiser-test.nl/content/rambles/mpd-and-icecast-on-nix.md

5.9 KiB

title date author tags description
Mpd and Icecast on Nix 2022-11-06T22:11:10+01:00 $HUMANOID
nix
linux
Ramble about setting up MPD + Icecast on NixOS

Introduction

For quite some time now, I've been wanting to move my home server from Debian to NixOS. There were a few things that kept me from starting with the migration. The main one being that the Debian installation still worked reasonably well. I only had occasional hickups in regards to the two USB disks hooked up to my Raspberry Pi 4. Every now and then they'd disappear. My guess is that it was due to power draw as it would consistently happen when putting heavy IO loads on either disk. One of these disks is home to my music collection and used by my internet radio setup (in other words, the main thing I listen to when at home). And this nicely brings my to my next issue.

{{< img class="stickers" src="/images/graphs/radio.png" mouse="Flowchart demonstrating software stack">}}

This radio consists of an MPD instance streaming to an Icecast2 instance, being reverse proxied through an NGINX instance (future plans include managing the music collection through Git Annex...). I have tried setting up MPD on NixOS in the past, but couldn't get it to work for some odd reason. I don't remember why I never managed it. I just know that I gave up after a few hours. Today I decided to spend as much time as necessary to get at least the MPD + Icecast2 portion to work. The rest of this post will be detailing how I went about setting configuring MPD + Icecast2 on NixOS as there were a few non-obvious things I bumped into.

Setting up MPD

To prevent my /etc/nixos/configuration.nix from ballooning into absurdity, you'll want to create something like a /etc/nixos/services/radio.nix file containing everything related to the radio stack. This file is then imported in /etc/nixos/configration.nix in the imports section as follows:

{ config, pkgs, ... }: {
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix
      ./drives.nix
      ./services/radio.nix

      ...

    ];
...

}

The first thing you'll want to do is enable the MPD service:

{ config, pkgs, ... }: {
  services.mpd = {
    enable = true;
    ...

From here, there are a few rather basic options that you'll want to set:

...
    musicDirectory = "/path/to/music-collection/";
    network.listenAddress = "any"; # not quite sure if this is needed
...

At the extraConfig section is where things get interesting. Here I assumed you could point Nix to an existing config file with (builtins.readFile /path/to/mpd.conf) but it seems I am wrong. It doesn't throw any errors when switching to a new build, but it doesn't read the given config file either. I'm probably missing something here. Please shout at me if you happen to know what it is.

Regardless, you're going to want to add at least one audio output method. Since we're going to make MPD talk to Icecast2, we're going to add audio_output section of type "shout":

...
    musicDirectory = "/path/to/music-collection/";
    extraConfig = ''
      audio_output {
          type            "shout"
          encoder         "vorbis" # FOSS codec for the win
          name            "My Shouty Stream"
          host            "<name found in the networking.hostName variable>"
          port            "8000" # or use whatever meme you like
          mount           "/mpd.ogg"
          password        "hunter2"
          quality         "5.0"
          format          "44100:16:2"
          genre           "Rythmic Noise!"
          protocol        "icecast2"
      }
    '';
  };
...

Please don't take this snippet as gospel and read through the MPD configuration. Whatever is found in extraConfig will be directly given to MPD as if through /etc/mpd.conf on e.g. Debian. For instance, if you only locally want to listen to your music through say PulseAudio, than you'll need a corresponding output section.

{{< start-details summary="Example PulseAudio configuration I have on my laptop" >}}

...
audio_output {
    type            "pulse"
    name            "Pulse output"
    audio_output_format             "44100:16:2"
    samplerate_converter            "Medium Sinc Interpolator"
    mixer_type                      "software"
    replaygain                      "album"
    volume_normalization            "no"
}
...

{{< end-details >}}

Mounting a music disk

In order to automount a disk on NixOS, add an expression something like the following (indirectly) in your /etc/nixos/configration.nix:

fileSystems."/srv/music" = {
  device = "/dev/sdb1"; # assuming that sdb1 is the partition with your music on it
  # device = "/dev/disk/by-label/Sauserer"; # or ideally, if you have a labelled disk
  fsType = "ext4"; # assuming it has a ext4 filesystem on it
};

I have my drives listed in /etc/nixos/drives.nix and import that in my configration.nix

Setting up Icecast2

The next step is to set up Icecast2. Same as with MPD, the first thing you'll want to do is enable it:

...
  services.icecast = {
    enable = true;
    extraConf = ''
    <location>Floating around in an example configuration snippet on the internet</location>
    <admin>icemaster@localhost</admin>
    <authentication>
        <source-password>hunter2</source-password>
        <relay-password>even-more-super-secret-password</relay-password>
    </authentication>
   
    <limits>
        <clients>100</clients>
        <sources>2</sources>
        <queue-size>524288</queue-size>
        <client-timeout>30</client-timeout>
        <header-timeout>15</header-timeout>
        <source-timeout>10</source-timeout>
        <burst-on-connect>1</burst-on-connect>
        <burst-size>65535</burst-size>
    </limits>
    '';
    hostname = "hostname:8000";
    listen.port = 8000;
    admin.password = "it gives me a headache that I can't get Nix to read this from a file";
  };
}