For a while now, I’ve been looking for a good way to stream music from my home music collection on my phone.
There are quite a few options for music servers that support streaming. However, Android apps that can stream music from one of those servers tend to be unmaintained, clunky or slow (or more than one of those).
It is possible to use something that runs in a web server, but that means no offline caching - which can be quite convenient in spots without connectivity, such as the Underground or other random bits of London with poor cell coverage.
Most music servers today support some form of the subsonic API.
I’ve tried a couple, with mixed results:
- supysonic; Python. Slow. Ran into some issues with subsonic clients. No real web UI.
- gonic; Go. Works well & fast enough. Minimal web UI, i.e. no ability to play music from a browser.
- airsonic; Java. Last in a chain of (abandoned) forks. More effort to get to work, and resource intensive.
Eventually, I’ve settled on Navidrome. It’s got a couple of things going for it:
- Good subsonic implementation that worked with all the Android apps I used it with.
- Great Web UI for use in a browser
I run Navidrome in Kubernetes. It’s surprisingly easy to get going. Here’s the deployment I’m using:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
apiVersion: apps/v1 kind: Deployment metadata: name: navidrome spec: replicas: 1 selector: matchLabels: app: navidrome template: metadata: labels: app: navidrome spec: containers: - name: navidrome image: deluan/navidrome:latest imagePullPolicy: Always resources: limits: cpu: ".5" memory: "2Gi" requests: cpu: "0.1" memory: "10M" ports: - containerPort: 4533 volumeMounts: - name: navidrome-data-volume mountPath: /data - name: navidrome-music-volume mountPath: /music env: - name: ND_SCANSCHEDULE value: 1h - name: ND_LOGLEVEL value: info - name: ND_SESSIONTIMEOUT value: 24h - name: ND_BASEURL value: /navidrome livenessProbe: httpGet: path: /navidrome/app port: 4533 initialDelaySeconds: 30 periodSeconds: 3 timeoutSeconds: 90 volumes: - name: navidrome-data-volume hostPath: path: /srv/navidrome type: Directory - name: navidrome-music-volume hostPath: path: /srv/media/music type: Directory --- apiVersion: v1 kind: Service metadata: name: navidrome spec: ports: - port: 4533 name: web selector: app: navidrome type: ClusterIP
At the moment, this deployment is still tied to the machine with my music on it since it relies on hostPath volumes, but I’m planning to move that to ceph in the future.
I then expose this service on /navidrome on my private domain (here replaced with example.com) using an Ingress:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: navidrome spec: ingressClassName: nginx rules: - host: example.com http: paths: - backend: service: name: navidrome port: name: web path: /navidrome(/|$)(.*) pathType: Prefix
On the desktop, I usually just use navidrome’s web interface. Clementine’s support for subsonic is also okay. sublime-music is meant to be a music player specifically for Subsonic, but I’ve not really found it stable enough for day-to-day usage.
There are various Android clients for Subsonic, but I’ve only really considered the Open Source ones that are hosted on F-Droid. Most of those are abandoned, but D-Sub works pretty well - as does my preferred option, Subtracks.