Code, Compose, Bike, Brew


…though not necessarily in that order

Running Logitech Media Server in Docker

| Comments

I recently signed up for Spotify’s 30 day free trial, having such convenient access to so much music was great, but when it came to an end and I was faced with the prospect of paying for my first month I was reminded just how little Spotify actually pays musicians per play. Despite being a software engineer in a very digital age I have always preferred to buy music on a physical artefact on Vinyl or CD. I prefer to have the artefact itself as I find it gives me a different listening experience when compared to digital media, I tend to give the music more time and attention if I sit down and listen to the CD or record vs just bunging some ear phones in and playing some tunes on my phone. Where possible I also prefer to buy from the artist or their label direct to make sure that they are adequately compensated for their work so that they can earn a living and can carry on making the music that I love. Finally having the physical CD also gives me the ability to control how I store the music in my digital library e.g. what software and format I use to rip the physical media.

So having said no to Spotify I found myself missing the ability to choose from a wide range of Music when on the go, so I thought I’d set up Logitech Media Server (LMS) (formally known as ‘Squeezeserver’ or ‘Slimserver’) on my Fitlet-h. I’m sharing my notes on how I went about this in case it’s useful to anyone.

Docker

I wanted to try running the Squeezeserver in Docker, a quick search on the Docker Hub showed that there is already a Docker image which does just that. So let’s see if we can run it locally first of all:

mkdir -p /tmp/squeezebox # This is just a temporary directory in which the Squeezeserver will store its state
export MY_MUSIC_DIRECTORY=/Data/Music # This is the path to my music directory on my local machine
docker run -d \
           -p 9000:9000 \
           -p 9090:9090 \
           -p 3483:3483 \
           -p 3483:3483/udp \
           -v /tmp/squeesezerver/state:/srv/squeezebox \
           -v ${MY_MUSIC_DIRECTORY}:/srv/music \
           larsks/logitech-media-server

Note: the docker image name is specified incorrectly on the Docker Hub page as larsks/logitech/media-server. It is in fact larsks/logitech-media-server

Let’s see if it’s running:

➜  ~ sudo docker ps                   
CONTAINER ID        IMAGE                          COMMAND             CREATED              STATUS              PORTS                                                                                            NAMES
9a8f50ea19c5        larsks/logitech-media-server   "/entrypoint.sh"    About a minute ago   Up About a minute   0.0.0.0:3483->3483/tcp, 0.0.0.0:9000->9000/tcp, 0.0.0.0:9090->9090/tcp, 0.0.0.0:3483->3483/udp   amazing_mccarthy

So far so good. Opening a browser at http://127.0.0.1:9000/ brought up the LMS interface and everything worked perfectly.

Ansible

So we’ve got a Docker image which we think is good enough to use, the next step is to orchestrate the provisioning of this container on to the host that we want to run LMS from.

The outline of what we want to do is something like:

  • Install Docker
  • Pull the image
  • Start the image, exposing the relevant ports and mounting the appropriate paths etc
  • Do a bit of extra configuration to ensure the container starts on boot etc.

I’m a big fan of Ansible and it seemed like a natural fit here, so that’s what I’ve used.

Installing Docker

A quick check of Ansible Galaxy brought back quite a few Docker roles. I had a quick scan of them and settled on angstwad.docker_ubuntu which seems to be very well written is very comprehensive in that it does all of the required post-installation configuration required in order to get Docker to work.

To pull and install the role locally I did this:

ansible-galaxy install angstwad.docker_ubuntu

Writing the Playbook

Next task was to write a basic playbook to do the necessary bits and pieces, here’s a very basic play which covers all of the steps required.

# Install logitech-media-server
- hosts: fitlet
  vars:
    logitech_media_server_base_directory: /Data/logitech-media-server

  pre_tasks:
    - file:
        path=""
        state=directory
        recurse=true
        owner=root
        group=root
        mode=0775
      become: true
      tags: logitech

  roles:
    - { role: angstwad.docker_ubuntu, become: true, tags: logitech}

  post_tasks:
    - docker_container:
        name: logitech-media-server
        image: larsks/logitech-media-server
        published_ports: ["3483:3483", "9000:9000", "9090:9090"]
        volumes:
              - /Data/Music:/srv/music
              - ":/srv/squeezebox"
        restart_policy: unless-stopped
      become: true
      tags: logitech

Run the Playbook

Now we’ve got our playbook it’s time to run it against the target host, in my case the magic incantation is:

ansible-playbook -i inventories/dev/dev.ini fitlet.yml -vv --tags logitech

Having provisioned the host we should now be able to acess the LMS webapp on port 9000 of the IP address that it has bound to, in my case this was the IP on eth0 which gave me a URL of http://192.168.0.2:9000

Secure it with a username and password

Important note: The security that comes with LMS is terrible, it runs over http only and uses basic auth, which as I’m sure you know is a completely insecure combination. In its defense, I would imagine that exposing one’s music collection over the internet was not a key design consideration. However given that we’re just playing here I’ll continue to detail the steps I followed to make it accessible to me from anywhere, but please, if you set this up yourself remember that the username and password you use here should not be considered secure.

The security menu is buried several levels down in the UI, to access it navigate through the following:

  • Settings button (Bottom right hand corner)
  • Advanced tab
  • Click the twisty on the left hand side (which probably says ‘Formatting’)
  • Select ‘Security’
  • Configure the page appropriately for your needs.

Making it accessible from anywhere

Now that we’ve ‘secured’ the UI we can make it accessible from the outside world, the exact steps required will vary from setup to setup but a simple setup probably won’t be a million miles away from the following:

Export ports

Expose the following ports and protocols on your WAN interface:

  • 3484 tcp (for connections from players) and udp (for SlimProto commands)
  • 9000 tcp (for http access to the webapp)
  • 9090 tcp (for telnet *shudder* access to the CLI)

Configure Dynamic DNS (Optional)

Unless you have a static IP address you’ll probably want to configure Dynamic DNS to point a particular DNS name to your WAN interface e.g. music.mydomain.com. Once you’ve done this you should be able to access the LMS webapp at the DNS name you’ve configured e.g. http://music.mydomain.com:9000

Get an App

The final step I took was to configure my Android phone as a LMS ‘player’, this typically requires 2 apps: The first app talks the LMS protocols and registers your device as a ‘player’. The 2nd app provides a UI of your music collection to you and provides the ‘controls’ e.g. play/ pause/ volume/ search etc.

I use Squeezeplayer as the controller app and Squeezer as the player app, I find this combination of apps to work really well. I have also tried the official Logitech Squeezebox Controller app in the past but couldn’t get it working reliably as it seems to require your local LMS server to be on a local network, which of course in this case is not the case. I have also tried Squeeze Commander but recent versions seems to have stopped working for me and I found its UI quite unintuitive.

I don’t use Apple devices so I’ve no idea what apps are available here but if you do then let me know and I’ll happily add some notes here.

Future tweaks:

Functionally speaking the above works really well, I have access to my entire music collection anywhere I have my phone and can stream over $g or Wireless depending on what’s available. However the security issue really irks me, so perhaps a next step would be to see if it’s possible to put LMS behind something which will do TLS termination (perhaps nginx)… I’d be interested to hear from anyone who has done any securing or hardening of LMS so drop me a line in the comments if you have and don’t mind sharing the details of your implementation.

That’s it for this entry, I hope this helps someone achieve portability of their music library, please drop me a line in the comments if you spot any inaccuracies or have any questions or comments.

Cheers,

Edd

Comments