Introduction
The majority of us listen to music while working in the office, at home, or while traveling. Although Spotify, Apple Music, and Google’s YouTube Music are highly popular music streaming applications, each necessitates a paid subscription. It’s also possible that you don’t depend on these subscriptions and have accumulated hundreds of MP3 audio files over the years, which you can only play on your phone by copying them to the local storage. This is simple, but what if you want to keep the same playlist on all of your devices?
Background
I’m not a guy who is into audiophiles or music. While I do listen to various genres of music on YouTube and while on the go, I am not overly concerned with the way I organize my collection. In the past, I was too lazy to download new songs and upload them to my phone, so I would just move them to my local storage and listen to the same playlist nonstop for months. After I began searching for self-hosted applications to install on my K3s cluster, everything changed. I discovered the Navidrome project, which is a self-hosted, open-source music server that can be used to stream and manage your own music library similarly to Spotify. I know this isn’t an essential lifestyle app, but I was in favor of having my own hosting app since I already paid for a virtual private server (VPS) every year.
Prerequisite
- A remote VPS, local server, or Network Attached Storage (NAS) to host Navidrome and Syncthing (explained below)
- Network access (ideally a public IP with a domain name) to access your music library outside your home
- Music files ^.^
Step 1 – Navidrome Kubernetes Manifest
Navidrome v0.58.0 introduces multi-library support, a major architectural enhancement that enables users to organize and manage multiple music collections with proper permission controls and complete UI integration. Imagine having a song library for your children as they grow up, which will continue to exist even after mobile phones have been upgraded. Now, Navidrome does not offer official Helm charts; even if you locate community-maintained Helm charts, they are likely outdated with regard to (see Navidrome Configuration Options). The following is a Kubernetes Manifest YAML script that I employed to deploy Navidrome v0.58.0 in my K3s Kubernetes cluster.
apiVersion: v1
kind: Namespace
metadata:
name: navidrome
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: navidrome-data-pvc
namespace: navidrome
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 200Mi
storageClassName: longhorn
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: navidrome-music-pvc
namespace: navidrome
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: longhorn
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: navidrome
namespace: navidrome
labels:
app: navidrome
spec:
replicas: 1
selector:
matchLabels:
app: navidrome
template:
metadata:
labels:
app: navidrome
spec:
containers:
- name: navidrome
image: deluan/navidrome:0.58.0
imagePullPolicy: IfNotPresent
env:
- name: ND_MUSICFOLDER
value: /music
- name: ND_DATAFOLDER
value: /data
- name: ND_LOGLEVEL
value: info
- name: ND_SESSIONTIMEOUT
value: 24h
- name: ND_SCANNER_SCHEDULE
value: "@every 15m"
# Optional new settings in 0.58:
- name: ND_ENABLETRANSCODINGCONFIG
value: "true"
- name: ND_BASEURL
value: ""
- name: ND_ENABLESHARING
value: "true"
- name: ND_ENABLEUSEREDIT
value: "true"
ports:
- containerPort: 4533
volumeMounts:
- mountPath: /data
name: data-volume
- mountPath: /music
name: music-volume
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: navidrome-data-pvc
- name: music-volume
persistentVolumeClaim:
claimName: navidrome-music-pvc
---
apiVersion: v1
kind: Service
metadata:
name: navidrome
namespace: navidrome
spec:
selector:
app: navidrome
ports:
- protocol: TCP
port: 4533
targetPort: 4533
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: navidrome-ingress
namespace: navidrome
annotations:
cert-manager.io/cluster-issuer: letsencrypt-dns
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/hsts: "true"
nginx.ingress.kubernetes.io/hsts-max-age: "31536000"
nginx.ingress.kubernetes.io/hsts-include-subdomains: "true"
nginx.ingress.kubernetes.io/hsts-preload: "true"
nginx.ingress.kubernetes.io/limit-connections: "20"
nginx.ingress.kubernetes.io/limit-rps: "10"
nginx.ingress.kubernetes.io/limit-burst-multiplier: "5"
nginx.ingress.kubernetes.io/x-frame-options: "DENY"
nginx.ingress.kubernetes.io/x-content-type-options: "nosniff"
nginx.ingress.kubernetes.io/referrer-policy: "strict-origin-when-cross-origin"
spec:
ingressClassName: nginx
tls:
- hosts:
- navidrome.example.com
secretName: navidrome-tls
rules:
- host: navidrome.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: navidrome
port:
number: 4533
Manifest Explanation
- Navidrome will be installed to
namespace: navidrome - Two
PersistentVolumeClaimwill be created to store Navidrome data (200Mi) and music files (1Gi). - The latest image is
image: deluan/navidrome:0.58.0at the time of writing this. - The
envvalues are configured as per Navidrome Configuration Options. - Take note of the
mountPath: /musicfor Syncthing later on. - Nginx ingress plus Let’s Encrypt to domain
host:that maps to Navidrome service at TCPport: 4533
K3s Rancher Dashboard: Pods, PVC, Services



Step 2 – Syncthing Kubernetes Manifest
Syncthing is a peer-to-peer file synchronization tool that is open-source and does not require cloud services. It enables users to directly sync files between devices. Key features include peer-to-peer sync, cross-platform, open source & privacy-focused, versioning, encrypted communication, and even the incorporation of a web GUI and API. Its core purpose is to
- Keep files synchronized across multiple devices automatically.
- Works directly between your devices using your network or the internet.
- No central server is required, though you can run a dedicated node if desired.
Because Navidrome is exclusively a music streaming server and does not offer file upload functions or storage management, we require Syncthing to transfer our local music files to the remote Navidrome /music folder. Standard ways to copy music files directly into Navidrome’s music folders include SFTP/SCP, SMB/NFS share, and more. But since we’re using PVC (Persistent Volume Claim) and Kubernetes pods, an app like Syncthing will simplify our lives by automatically mirroring our local music folder to Navidrome’s music folder.
apiVersion: apps/v1
kind: Deployment
metadata:
name: syncthing
namespace: navidrome
spec:
replicas: 1
selector:
matchLabels:
app: syncthing
template:
metadata:
labels:
app: syncthing
spec:
containers:
- name: syncthing
image: syncthing/syncthing:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8384 # Web UI
- containerPort: 22000 # Sync
# env section not required, because
# 1. The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.
# 2. Can set GUI Authentication User and Authentication Password
#env:
#- name: STGUIADDRESS
# value: "0.0.0.0:8384"
#- name: STGUIAUTH
# valueFrom:
# secretKeyRef:
# name: syncthing-gui-secret
# key: STGUIAUTH
volumeMounts:
- name: syncthing-config
mountPath: /config
- name: music
mountPath: /music
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: syncthing-config
persistentVolumeClaim:
claimName: syncthing-config-pvc
- name: music
persistentVolumeClaim:
claimName: navidrome-music-pvc
---
apiVersion: v1
kind: Service
metadata:
name: syncthing
namespace: navidrome
spec:
selector:
app: syncthing
ports:
- name: http
protocol: TCP
port: 8384 # UI port
targetPort: 8384
- name: sync
protocol: TCP
port: 22000 # Sync protocol
targetPort: 22000
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: syncthing-ingress
namespace: navidrome
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-dns"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
# Optional security headers
nginx.ingress.kubernetes.io/hsts: "true"
nginx.ingress.kubernetes.io/hsts-max-age: "31536000"
nginx.ingress.kubernetes.io/hsts-include-subdomains: "true"
nginx.ingress.kubernetes.io/hsts-preload: "true"
nginx.ingress.kubernetes.io/x-frame-options: "DENY"
nginx.ingress.kubernetes.io/x-content-type-options: "nosniff"
nginx.ingress.kubernetes.io/referrer-policy: "strict-origin-when-cross-origin"
spec:
ingressClassName: nginx # <---- here
rules:
- host: syncthing.example.com # <== Change to your actual domain
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: syncthing
port:
number: 8384
tls:
- hosts:
- syncthing.example.com
secretName: syncthing-tls
---
# Syncthing needs a directory to store its own config (GUI settings, device IDs, folder list, keys, etc).
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: syncthing-config-pvc
namespace: navidrome
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
storageClassName: longhorn
The deployment YAML is structurally similar to Naivdrome’s above, except it uses 1 PVC only.
Step 3 – Using Syncthing for Navidrome
- Navidrome should be up and running, and an admin user should be created when the Navidrome webUI is first loaded. No playlists or sample songs are available for testing or demonstration purposes.
- Make sure that remote Syncthing is up and running. When we open the Syncthing web URL, it will ask us to create a GUI authentication user and password. I suggest that you do this and also check the box that says “Use HTTPS for GUI“
- Download Syncthing for your local setup; the Syncthing Windows Setup, a feature-rich yet portable Windows installer, is used in this tutorial.
- In Windows, run the Start Syncthing app, then click the Syncthing Configuration Page desktop shortcut or open
http://127.0.0.1:8384/in your browser. There is no need to create GUI authentication or tick the Use HTTPS for GUI checkbox because the ‘local’ app can only be accessed within your home network. - At the local Syncthing webUI, click + Add Folder > Enter Folder Label (for example, techsch-music) and Folder Path (for example, C:\Users\Techsch\Music) > Save
- At the local Syncthing webUI, click + Add Remote Device > Copy remote Syncthing Device ID by clicking Actions > Show ID > Copy > Paste into local Syncthing Device ID > Sharing tab > Shared Folders > Check folder: techsch-music > Give a Device Name (for example, Navidrome-remote) > Save
- Between both local and remote Syncthing web GUIs, in the Folders and Remote Devices section, click Edit > Sharing tab > Check music folder and device respectively > Save
- Within a minute or two, the sync job will run automatically, and then refresh in Navidrome webUI to see the “Recently Added” songs.
This process may appear tedious; however, it is necessary to configure it once initially. Subsequently, the Start Syncthing service will automatically transfer your music files to the Navidrome library.
How to increase connection sync transfer speed?
If you see Connection Type: Relay WAN, then the sync transfer speed will be slow. This would mean your local and remote Syncthing could not establish a direct peer-to-peer (P2P) connection, so Syncthing is routing traffic through a public relay server over the internet (Wide Area Network = WAN).

How to fix “Relay WAN” and get a FAST direct connection?

- Port Forwarding
– On your NAS or local PC side, forward TCP port 22000 to the machine running Syncthing.
– Also open UDP port 22000 if possible (helps with QUIC protocol).
– Make sure your router/firewall allows inbound on these ports. - Log into your router:
– For Asus, Advanced Settings > NAT Forwarding > Port Forwarding
– Service Name: syncthing
– Source IP: Leave blank or 0.0.0.0
– External Port: 22000
– Internal Port: 22000
– Internal IP: Your PC’s local IP (Command Promptipconfig) or Device Name
– Protocol: TCP (and UDP if possible) - Allow Syncthing through Windows Firewall
– Open Windows Defender Firewall
– Click Allow an app or feature through Windows Defender Firewall
– Find Syncthing in the list and make sure it’s allowed on private and public networks
– If not listed, click Allow another app… and add Syncthing manually
– Also, allow port 22000 TCP and UDP inbound rules if you want to be extra sure.
The connection should now be set to TCP or QUIC (WAN). Alternatively, you may restart both the local and remote Syncthing app. I only have about 100 music files in my library, and it only takes me 1-2 seconds for a 10 MB *.mp3 file to sync with my TCP (WAN) connection.
Troubleshooting
The only major issue I encountered was that Syncthing was unable to write music files into Navidrome’s /music PVC due to permission issues.2025-10-21 23:38:12: Failed to create path for auto-accepted folder syn223j-music-syncthing at path syn223j-music-syncthing: mkdir /syn223j-music-syncthing: permission denied
ChatGPT recommends adding securityContext, runAsUser and runAsGroup and kubectl exec -it pod/syncthing-7c6fbb7c47-gm8wx -n navidrome -- chown -R 1000:1000 /music && chmod -R 755 /music but none resolved the problem. What I did was to delete all the folders and devices back to the initial state in Syncthing WebUI, and I went to reload the Navidrome pods in my K3s. Then I repeated Step 3 – Using Syncthing for Navidrome—after which everything fell into place.

Step 4 – Navidrome Clients
Navidrome implements a substantial portion of the Subsonic API. There are a few, but not many, Subsonic clients in the app market, depending on whether your phone is Android or iOS. For the iOS App Store, there are SubStreamer and Amperfy Music. I only manage to find Symfonium app available in the Google Play Store; after the trial period ends, it becomes a paid app, but many users say it’s worth the money.
Conclusion
Honestly, I don’t use Navidrome every day; instead, I go to YouTube and choose songs based on how I’m feeling. If you are abroad, you will be wasting valuable paid roaming data because Navidrome needs a data connection to stream your music while you are away from your local telco. There are other uses for Syncthing, such as syncing your local backups to the cloud virtually instantly, but I’ve already implemented my own 3-2-1 backup plan, which backs up my data to cloud object storage using the Synology NAS Hyper Backup app. Because it is easier to copy files to an Android device than an iPhone, I will continue to host my Navidrome and Syncthing project because I find it convenient to stream audio files without having to copy them to my iPhone storage!
