Navidrome Playlists Backup

This role backs up Navidrome music playlists using the Navidrome API.

ARA Ansible Bash HTTPS JSON NAS Navidrome Proxmox

Navidrome Playlists Backup Role

Overview

This role backs up Navidrome music playlists using the Navidrome API. It authenticates to Navidrome, downloads each playlist as M3U files, and stores them on dual NAS storage with dated directories for organization. The role automatically manages retention by keeping only the last 5 backup directories per NAS.

Purpose

  • Playlist Protection: Backs up curated music playlists
  • Migration Support: Export playlists for transferring to new Navidrome instance
  • Disaster Recovery: Restore playlists after data loss
  • Dual Redundancy: Backs up to two separate NAS devices (Synology DS418 and Proxmox OMV)
  • Dated Organization: Groups backups by date for easy identification
  • API-Based: Uses Navidrome’s native API with JWT authentication

Requirements

  • Ansible 2.9 or higher
  • Navidrome server accessible via HTTPS
  • Navidrome user credentials stored in Ansible Vault
  • NAS mount points must be accessible and writable
  • Network connectivity to Navidrome server

What is Navidrome?

Navidrome is a self-hosted music streaming server that provides:

  • Music library management and streaming
  • Playlist creation and management
  • Subsonic API compatibility
  • Web and mobile client support

Role Variables

Required Variables

VariableRequiredDescription
vault_navidrome_joffrey_userYesNavidrome username (stored in Ansible Vault)
vault_navidrome_joffrey_passwordYesNavidrome password (stored in Ansible Vault)
navidrome_playlists_backup_playlistsYesDictionary of playlist IDs and names

Optional Variables

VariableDefaultDescription
navidrome_playlists_backup_urlhttps://server.homelabNavidrome instance URL
navidrome_playlists_backup_syno_mount_point/mnt/synology-ds418/navidrome/playlistsSynology NAS backup destination
navidrome_playlists_backup_prxmxomv_mount_point/mnt/prxmx-omv/navidrome/playlistsProxmox OMV NAS backup destination

Variable Details

vault_navidrome_joffrey_user and vault_navidrome_joffrey_password

Navidrome login credentials. Must be stored in Ansible Vault.

Example vault variables:

vault_navidrome_joffrey_user: "joffrey"
vault_navidrome_joffrey_password: "YourSecurePassword123"

Dictionary mapping playlist IDs (UUIDs) to human-readable names.

How to find playlist IDs:

  1. Log into Navidrome web UI
  2. Click on a playlist
  3. Check URL: https://server.homelab/app/playlist/{playlist_id}

Example:

navidrome_playlists_backup_playlists:
  abc123-def456-ghi789: "Rock Classics"
  xyz789-uvw456-rst123: "Chill Vibes"
  mno345-pqr678-stu901: "Workout Mix"

The full URL to your Navidrome instance. Must include protocol (https://).

Example:

navidrome_playlists_backup_url: "https://music.example.com"

Dependencies

This role has no dependencies on other Ansible roles, but requires:

  • Navidrome server running and accessible
  • Valid user credentials in Navidrome
  • NAS mounts configured (see nas_mount or nas_mount_systemd roles)

Example Playbook

Basic Usage

---
- name: Backup Navidrome Playlists
  hosts: localhost
  become: true

  roles:
    - navidrome_playlists_backup

With Custom Configuration

---
- name: Backup Navidrome Playlists from Custom URL
  hosts: localhost
  become: true

  vars:
    navidrome_playlists_backup_url: "https://music.example.com"
    navidrome_playlists_backup_playlists:
      playlist-uuid-1: "My Favorites"
      playlist-uuid-2: "Workout Music"

  roles:
    - navidrome_playlists_backup

Scheduled Backup with Cron

---
- name: Schedule Weekly Navidrome Playlist Backup
  hosts: localhost
  become: true

  tasks:
    - name: Create cron job for weekly playlist backup
      ansible.builtin.cron:
        name: "Weekly Navidrome Playlist Backup"
        minute: "0"
        hour: "4"
        weekday: "0"  # Sunday
        job: "ansible-playbook /path/to/navidrome_backup.yml"

What This Role Does

  1. Creates dated backup directories on both NAS mounts (format: YYYY-MM-DD)
  2. Authenticates to Navidrome API (/auth/login)
  3. Extracts JWT token and client ID from response
  4. Fails gracefully if authentication fails
  5. Downloads each playlist as M3U file from /api/playlist/{id}/tracks
  6. Saves to Synology NAS with timestamped filename
  7. Saves to Proxmox OMV NAS with timestamped filename
  8. Finds all backup directories on each NAS
  9. Removes old directories, keeping only the 5 most recent per NAS

Authentication Flow

Step 1: Authenticate

POST /auth/login
Body: {
  "username": "user",
  "password": "password"
}

Response:

{
  "id": "user-uuid",
  "token": "jwt-token-here",
  "username": "user",
  ...
}

Step 2: Extract Token

The role extracts:

  • token: JWT for authentication
  • id: Client ID for requests

Step 3: Download Playlists

GET /api/playlist/{playlist_id}/tracks
Headers:
  Accept: audio/x-mpegurl
  x-nd-authorization: Bearer {jwt_token}
  x-nd-client-unique-id: {client_id}

Response: M3U playlist file

Backup Organization

Directory Structure

synology-ds418/navidrome/playlists/
├── 2026-01-07/
│   ├── Alt_Rock_And_Metal_20260107_143022.m3u
│   ├── Guitar_20260107_143022.m3u
│   └── The_Very_Best_Of_20260107_143022.m3u
├── 2026-01-06/
│   └── ...
└── 2026-01-05/
    └── ...

File Naming

{sanitized_playlist_name}_{timestamp}.m3u

Examples:

  • Alt_Rock_And_Metal_20260107_143022.m3u
  • Melodic_Death_Metal_20260107_143022.m3u
  • The_Very_Best_Of_20260107_143022.m3u

Special characters are replaced with underscores for filesystem safety.

M3U File Format

#EXTM3U
#EXTINF:240,Artist Name - Song Title
/path/to/music/file1.mp3
#EXTINF:180,Another Artist - Another Song
/path/to/music/file2.mp3

M3U files contain:

  • Track duration
  • Artist and title metadata
  • File paths (absolute)

Backup Content

The backup includes:

  • Playlist track listings (order preserved)
  • Track metadata (artist, title, duration)
  • File paths to music files

The backup does NOT include:

  • Actual music files (only playlists)
  • Playlist descriptions or artwork
  • Play counts or statistics
  • User-specific data

Backup Retention

The role implements directory-based retention:

  • Keeps: Last 5 dated directories per NAS
  • Deletes: Directories older than the 5th most recent
  • Per-NAS logic: Each NAS independently maintains 5 directories

Example retention (keeps these):

2026-01-07/  ← Keep (newest)
2026-01-06/  ← Keep
2026-01-05/  ← Keep
2026-01-04/  ← Keep
2026-01-03/  ← Keep
2026-01-02/  ← Delete (too old)
2026-01-01/  ← Delete (too old)

Restoration

To restore playlists:

Manual Method

  1. Copy M3U files to Navidrome’s music directory
  2. Navidrome will automatically scan and import them

Import via Navidrome UI

  1. Upload M3U file in Navidrome settings
  2. Navidrome parses and creates playlist

Note: File paths in M3U must match your current music library structure.

Security Considerations

  • Credential Protection: Username/password marked with no_log: true
  • Vault Storage: Credentials must be in Ansible Vault
  • Token Protection: JWT token marked with no_log: true
  • HTTPS Required: Always use HTTPS for Navidrome connections
  • File Permissions: M3U files created with default permissions

Tags

This role does not define any tags. Use playbook-level tags if needed:

- hosts: localhost
  roles:
    - navidrome_playlists_backup
  tags:
    - backup
    - navidrome
    - music
    - playlists

Notes

  • Downloads playlists sequentially (one at a time)
  • Dated directories allow multiple backups per day
  • Special characters in playlist names are sanitized
  • Empty playlists create empty M3U files
  • Role is idempotent - safe to run daily

Troubleshooting

”Authentication failed - no token received”

Verify credentials:

  • Check username/password are correct in Navidrome
  • Ensure user account is active
  • Test login via Navidrome web UI

Test manually:

curl -X POST -H "Content-Type: application/json" \
  -d '{"username":"user","password":"pass"}' \
  https://server.homelab/auth/login

“Permission denied” when writing to NAS

Ensure:

  • NAS is mounted: mount | grep synology
  • Backup directories are writable
  • User has write permissions

Playlist download returns empty file

Check:

  • Playlist actually has tracks in Navidrome UI
  • Playlist ID is correct (check URL in browser)
  • Music files still exist in library

”Connection refused” errors

Verify:

  • Navidrome is running
  • URL is correct: curl -I https://server.homelab
  • Network connectivity

Wrong playlists backed up

Verify playlist IDs:

  1. Open Navidrome in browser
  2. Click each playlist
  3. Check URL for correct UUID
  4. Update navidrome_playlists_backup_playlists dictionary

Performance Considerations

  • Download speed depends on playlist size
  • Small playlists (< 100 tracks): ~1-2 seconds each
  • Large playlists (1000+ tracks): ~5-10 seconds each
  • Sequential downloads (not parallel)

Typical performance:

  • 5 playlists (avg 50 tracks): ~10-15 seconds
  • 10 playlists (avg 100 tracks): ~30-40 seconds
  • Navidrome 0.47+: Full support
  • Navidrome 0.48+: Full support
  • Navidrome 0.49+: Full support (latest)

The role uses stable API endpoints that haven’t changed.

API Endpoints Used

1. Authenticate

POST /auth/login
Content-Type: application/json

Body:
{
  "username": "user",
  "password": "password"
}

2. Download Playlist

GET /api/playlist/{playlist_id}/tracks
Accept: audio/x-mpegurl
x-nd-authorization: Bearer {jwt_token}
x-nd-client-unique-id: {client_id}

Finding Playlist IDs

Method 1: Browser URL

  1. Log into Navidrome
  2. Click on playlist
  3. URL shows ID: https://server.homelab/app/playlist/{ID}

Method 2: Browser DevTools

  1. Open DevTools (F12)
  2. Go to Network tab
  3. Click on playlist
  4. Find API request: /api/playlist/{ID}

Method 3: API

# Get all playlists
curl -H "x-nd-authorization: Bearer YOUR_TOKEN" \
  https://server.homelab/api/playlist

Alternative: Manual Backup

If you prefer manual backup:

# Authenticate
TOKEN=$(curl -s -X POST -H "Content-Type: application/json" \
  -d '{"username":"user","password":"pass"}' \
  https://server.homelab/auth/login | jq -r '.token')

CLIENT_ID=$(curl -s -X POST -H "Content-Type: application/json" \
  -d '{"username":"user","password":"pass"}' \
  https://server.homelab/auth/login | jq -r '.id')

# Download playlist
curl -H "Accept: audio/x-mpegurl" \
  -H "x-nd-authorization: Bearer $TOKEN" \
  -H "x-nd-client-unique-id: $CLIENT_ID" \
  -o "my_playlist.m3u" \
  "https://server.homelab/api/playlist/PLAYLIST_ID/tracks"

Best Practices

  1. Document playlist IDs in comments or documentation
  2. Schedule regular backups (weekly for active playlists)
  3. Verify M3U files occasionally
  4. Test restoration to ensure file paths are valid
  5. Update playlist dictionary when creating new playlists

License

MIT

Author

Created for homelab infrastructure management.