Deploying Navidrome on YunoHost with Docker (and Shared Permissions)
This guide documents the process of setting up Navidrome (an open-source music server) on a YunoHost VPS using Docker. The goal was to achieve a setup where:
- The service runs in a secure Docker container.
- Music files are stored in a shared directory (
/home/shared/Music) accessible by all YunoHost users. - The service sits behind Nginx with a custom domain and SSL.
1. The Challenge: Permissions
The main hurdle when running Docker containers on YunoHost is mapping the user permissions correctly. If you run the container as a default user (like root), you may encounter permission errors when trying to access folders owned by your system users, or vice versa.
Finding the Correct User IDs (UID/GID)
To allow the specific user (kalvin0x58c) to manage the container, and the all_users group to upload music, we first identified the correct IDs on the host system:
# Check the specific user ID
id kalvin0x58c
# Output example: uid=36200(kalvin0x58c) ...
In this deployment:
- UID (User ID):
36200(The main admin/user) - GID (Group ID):
4002(The YunoHostall_usersgroup)
2. Directory Setup
# Create the shared directory
mkdir -p /home/shared/Music
# Set ownership: User 36200 owns it, Group 4002 shares it
chown -R 36200:4002 /home/shared/Music
# Set permissions: Owner/Group have Read+Write, Others have Read
chmod -R 775 /home/shared/Music
3. Docker Configuration (docker-compose.yaml)
We placed the configuration in /opt/navidrome/docker-compose.yaml. Note the specific user: definition which maps the container's internal process to our host IDs.
services:
navidrome:
image: deluan/navidrome:latest
# Syntax is UID:GID.
# Using 36200 (User) and 4002 (Shared Group) ensures access to the shared folder.
user: 36200:4002
ports:
- "56896:4533" # Maps internal port 4533 to host 56896
restart: unless-stopped
environment:
ND_LOGLEVEL: info
volumes:
- "./data:/data" # Database stored locally in /opt/navidrome/data
- "/home/shared/Music:/music:ro" # Music folder mounted as Read-Only
4. Nginx Reverse Proxy Configuration
To expose the service at https://navidrome.obulou.org, we created a custom Nginx configuration.
Crucial fixes included:
- WebSocket Support: Required for the UI to update automatically.
- Timeout Increases: Preventing timeouts during large initial library scans.
- Client Body Size: Set to
0(unlimited) to prevent upload errors if the API is used for uploads.
The Reverse Proxy Block
*File location: /etc/nginx/conf.d/navidrome.obulou.org.d/reverseproxy.conf*
location @reverseproxy--proxy {
proxy_pass http://127.0.0.1:56896;
proxy_redirect off;
# Standard headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket Upgrade Magic
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Performance & Timeouts
client_max_body_size 0; # Allow unlimited file size
proxy_request_buffering off; # Stream immediately
proxy_buffering off;
proxy_connect_timeout 3600s; # High timeout for long scans
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;
}
location / {
try_files /dev/null @reverseproxy--proxy;
}
5. Troubleshooting: The "Permission Denied" Crash loop
The Error
After starting the container, the logs showed a restart loop with the following error:
FATAL: Error creating cache path: mkdir /data/cache: permission denied
The Cause
The ./data directory in /opt/navidrome was originally created by root during the first test run. When we switched the container to run as user 36200, it lost the ability to write to its own database folder.
The Fix
We manually fixed the ownership of the data directory on the host:
cd /opt/navidrome
chown -R 36200:36200 data
docker compose restart navidrome
Summary
By explicitly defining the user: UID:GID in Docker Compose and ensuring the host directory permissions matched, we achieved a secure, multi-user compatible music server.
No comments to display
No comments to display