From ae1d9522976ccf8013e7e73886fd39550bc82259 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Mon, 11 Aug 2025 20:43:28 -0500 Subject: [PATCH 1/2] music-assistant: Initial deployment _Music Assistant_ is pretty straightforward to deploy, despite upstream's apparent opinion otherwise. It just needs a small persistent volume for its media index and customization. It does need to use the host network namespace, though, in order to receive multicast announcements from e.g. AirPlay players, as it doesn't have any way of statically configuring them. --- music-assistant/ingress.yaml | 20 +++++++ music-assistant/kustomization.yaml | 21 ++++++++ music-assistant/music-assistant.yaml | 78 ++++++++++++++++++++++++++++ music-assistant/namespace.yaml | 6 +++ 4 files changed, 125 insertions(+) create mode 100644 music-assistant/ingress.yaml create mode 100644 music-assistant/kustomization.yaml create mode 100644 music-assistant/music-assistant.yaml create mode 100644 music-assistant/namespace.yaml diff --git a/music-assistant/ingress.yaml b/music-assistant/ingress.yaml new file mode 100644 index 0000000..35185e1 --- /dev/null +++ b/music-assistant/ingress.yaml @@ -0,0 +1,20 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: music-assistant + labels: + app.kubernetes.io/name: music-assistant + app.kubernetes.io/component: music-assistant +spec: + ingressClassName: nginx + rules: + - host: music.pyrocufflink.blue + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: music-assistant + port: + name: http diff --git a/music-assistant/kustomization.yaml b/music-assistant/kustomization.yaml new file mode 100644 index 0000000..9ce2daf --- /dev/null +++ b/music-assistant/kustomization.yaml @@ -0,0 +1,21 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: music-assistant + +labels: +- pairs: + app.kubernetes.io/instance: music-assistant + includeSelectors: true +- pairs: + app.kubernetes.io/part-of: music-assistant + includeTemplates: true + +resources: +- namespace.yaml +- music-assistant.yaml +- ingress.yaml + +images: +- name: ghcr.io/music-assistant/server + newTag: 2.6.0b18 diff --git a/music-assistant/music-assistant.yaml b/music-assistant/music-assistant.yaml new file mode 100644 index 0000000..5b8c752 --- /dev/null +++ b/music-assistant/music-assistant.yaml @@ -0,0 +1,78 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: music-assistant + labels: &labels + app.kubernetes.io/name: music-assistant + app.kubernetes.io/component: music-assistant +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + +--- +apiVersion: v1 +kind: Service +metadata: + name: music-assistant + labels: &labels + app.kubernetes.io/name: music-assistant + app.kubernetes.io/component: music-assistant +spec: + ports: + - port: 8095 + name: http + selector: *labels + +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: music-assistant + labels: &labels + app.kubernetes.io/name: music-assistant + app.kubernetes.io/component: music-assistant +spec: + serviceName: music-assistant + selector: + matchLabels: *labels + template: + metadata: + labels: *labels + spec: + containers: + - name: music-assistant + image: ghcr.io/music-assistant/server + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8095 + name: http + readinessProbe: &probe + httpGet: + port: http + path: / + failureThreshold: 3 + periodSeconds: 60 + successThreshold: 1 + timeoutSeconds: 1 + startupProbe: + <<: *probe + failureThreshold: 90 + periodSeconds: 1 + volumeMounts: + - mountPath: /data + name: music-assistant-data + subPath: data + dnsPolicy: ClusterFirstWithHostNet + hostNetwork: true + securityContext: + runAsNonRoot: true + runAsUser: 8095 + runAsGroup: 8095 + fsGroup: 8095 + volumes: + - name: music-assistant-data + persistentVolumeClaim: + claimName: music-assistant diff --git a/music-assistant/namespace.yaml b/music-assistant/namespace.yaml new file mode 100644 index 0000000..1f7c35f --- /dev/null +++ b/music-assistant/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: music-assistant + labels: + app.kubernetes.io/name: music-assistant From 42a7964991a133ba7d9e5f2a8db9ce428ec1de6f Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Mon, 11 Aug 2025 20:47:33 -0500 Subject: [PATCH 2/2] music-assistant: Tell players to restart on startup I haven't fully determined why, but when the Music Assistant server restarts, it marks the _shairport-sync_ players as offline and will not allow playing to them. The only way I have found to work around this is to restart the players after the server restarts. As that's pretty cumbersome and annoying, I naturally want to automate it, so I've created this rudimentary synchronization technique using _ntfy_: each player listens for notifications on a specific topic, and upon receiving one, tells _shairport-sync_ to exit. With the `Restart=` property configured on the _shairport-sync.service_ unit, _systemd_ will restart the service, which causes Music Assistant to discover the player again. --- music-assistant/kustomization.yaml | 19 +++++++++++++++++++ music-assistant/restart-sync.service | 22 ++++++++++++++++++++++ music-assistant/restart-sync.sh | 15 +++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 music-assistant/restart-sync.service create mode 100644 music-assistant/restart-sync.sh diff --git a/music-assistant/kustomization.yaml b/music-assistant/kustomization.yaml index 9ce2daf..f579ba0 100644 --- a/music-assistant/kustomization.yaml +++ b/music-assistant/kustomization.yaml @@ -16,6 +16,25 @@ resources: - music-assistant.yaml - ingress.yaml +patches: +- patch: | + apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: music-assistant + spec: + template: + spec: + containers: + - name: music-assistant + lifecycle: + postStart: + exec: + command: + - wget + - -qO- + - http://ntfy.ntfy:2586/0e386416-ac43-488c-b709-efac8e2555a3/publish?message=restart + images: - name: ghcr.io/music-assistant/server newTag: 2.6.0b18 diff --git a/music-assistant/restart-sync.service b/music-assistant/restart-sync.service new file mode 100644 index 0000000..025e8c8 --- /dev/null +++ b/music-assistant/restart-sync.service @@ -0,0 +1,22 @@ +# vim: set ft=systemd : + +# In order for this to be effective, `shairport-sync.service` needs a +# configuration override to enable auto-restart, e.g.: +# +# [Service] +# Restart=always +# RestartSec=1 + +[Unit] +Description=Restart shairport-sync when music-assistant restarts +Wants=network-online.target +After=network-online.target + +[Service] +Type=simple +ExecStart=/usr/local/libexec/restart-sync.sh +Restart=always +RestartSec=1 + +[Install] +WantedBy=shairport-sync.service diff --git a/music-assistant/restart-sync.sh b/music-assistant/restart-sync.sh new file mode 100644 index 0000000..a1d7e96 --- /dev/null +++ b/music-assistant/restart-sync.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +TOPIC=0e386416-ac43-488c-b709-efac8e2555a3 +NTFY=https://ntfy.pyrocufflink.net +DELAY=10 + +url=$(printf '%s/%s/raw' "${NTFY}" "${TOPIC}") + +curl -sN "${url}" \ + | stdbuf -o0 grep restart \ + | while read l; do +printf 'Restarting shairport-sync in %d seconds\n' "$DELAY" >&2 +sleep $DELAY +busctl call org.gnome.ShairportSync /org/gnome/ShairportSync org.gnome.ShairportSync Quit +done