Navidrome Scan
This role triggers music library scans in Navidrome using the Subsonic API.
Navidrome Scan Role
Overview
This role triggers music library scans in Navidrome using the Subsonic API. It generates secure authentication tokens using MD5 hashing with random salt, calls the Navidrome startScan API endpoint, supports both incremental and full scan modes, and displays scan status and statistics (tracks found, folders scanned) after triggering the scan.
Purpose
- Automated Library Scans: Trigger Navidrome scans via Ansible
- Integration with Workflows: Scan after adding new music files
- Incremental Scans: Detect new or modified files only
- Full Scans: Re-scan entire library from scratch
- Subsonic API: Uses standard Subsonic authentication protocol
- Status Reporting: Display scan progress and statistics
- Idempotent: Safe to run multiple times (starts new scan each time)
Requirements
- Ansible 2.9 or higher
- Navidrome server running and accessible via HTTPS
- Navidrome user credentials stored in Ansible Vault
- Network connectivity to Navidrome server
- Navidrome configured with music library paths
What is Navidrome?
Navidrome is a self-hosted music streaming server that:
- Organizes and streams your music collection
- Implements the Subsonic API (compatible with many music apps)
- Supports multiple audio formats (MP3, FLAC, OGG, etc.)
- Provides web UI and mobile apps
- Offers playlist management and scrobbling
- Requires periodic library scans to detect new music
Role Variables
Required Variables
| Variable | Required | Description |
|---|---|---|
vault_navidrome_joffrey_user | Yes | Navidrome username (in vault) |
vault_navidrome_joffrey_password | Yes | Navidrome password (in vault) |
Optional Variables
| Variable | Default | Description |
|---|---|---|
navidrome_scan_url | https://server.homelab | Navidrome server URL |
navidrome_scan_full | false | Full scan (true) or incremental (false) |
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"
navidrome_scan_url
The base URL to your Navidrome server.
Default:
navidrome_scan_url: "https://server.homelab"
Custom example:
navidrome_scan_url: "https://music.example.com"
navidrome_scan_full
Controls scan type: incremental or full.
Values:
false(default): Incremental scan (only new/modified files)true: Full scan (re-scan entire library)
Incremental scan (default):
navidrome_scan_full: false
Full scan:
navidrome_scan_full: true
When to use full scan:
- After major library reorganization
- When metadata changes aren’t detected
- Database corruption recovery
- First scan of new library
- Periodic maintenance (monthly)
When to use incremental scan:
- After adding new music files
- Daily/weekly automated scans
- Quick updates
- Minimal library changes
Dependencies
This role has no dependencies on other Ansible roles, but requires:
- Navidrome server installed and running
- Valid user account in Navidrome
- Music library configured in Navidrome
- Network access to Navidrome server
Example Playbook
Basic Usage (Incremental Scan)
---
- name: Trigger Navidrome Library Scan
hosts: localhost
become: false
roles:
- navidrome_scan
Full Library Scan
---
- name: Trigger Full Navidrome Library Scan
hosts: localhost
become: false
vars:
navidrome_scan_full: true
roles:
- navidrome_scan
Scan After Adding Music
---
- name: Copy Music and Scan Library
hosts: media_server
become: true
tasks:
- name: Copy new music files
ansible.builtin.copy:
src: /source/music/
dest: /mnt/music/library/
owner: music
group: music
- name: Wait for files to settle
ansible.builtin.pause:
seconds: 5
- name: Scan Navidrome Library
hosts: localhost
become: false
roles:
- navidrome_scan
Scheduled Weekly Scan
---
- name: Schedule Weekly Navidrome Scan
hosts: localhost
become: false
tasks:
- name: Create cron job for weekly library scan
ansible.builtin.cron:
name: "Weekly Navidrome Library Scan"
minute: "0"
hour: "3"
weekday: "0" # Sunday
job: "ansible-playbook /path/to/navidrome_scan.yml"
What This Role Does
-
Generates random salt (6-character hexadecimal)
- Used for Subsonic API authentication
- Prevents replay attacks
-
Calculates MD5 token
- Formula:
MD5(password + salt) - Subsonic API authentication method
- Formula:
-
Calls Subsonic API startScan endpoint:
- URL:
/rest/startScan - Method: GET
- Authentication: Token + salt
- Parameters: username, token, salt, format (json), version (1.8.0), client, fullScan
- URL:
-
Displays scan status:
- Scanning status (true/false)
- Scan type (incremental/full)
-
Displays statistics:
- Number of tracks found
- Number of folders scanned
Subsonic API Authentication
Authentication Method
Navidrome implements the Subsonic API authentication protocol:
Traditional (not used by this role):
?u=username&p=password
Token-based (used by this role):
?u=username&t=token&s=salt
Where:
username: Navidrome usernametoken: MD5 hash of (password + salt)salt: Random string
Why Token Authentication?
Security benefits:
- Password never sent in clear text
- Salt prevents replay attacks
- Each request uses unique token
- More secure than plain password in URL
Token Generation
# Pseudocode
salt = random_hex(6) # e.g., "a1b2c3"
token = md5(password + salt) # e.g., md5("MyPassword123a1b2c3")
# Final URL parameter:
# &t=d41d8cd98f00b204e9800998ecf8427e&s=a1b2c3
Subsonic API Endpoint
startScan Endpoint
GET /rest/startScan
Parameters:
| Parameter | Value | Description |
|---|---|---|
u | username | Navidrome username |
t | MD5 hash | Authentication token |
s | hex string | Random salt |
f | json | Response format |
v | 1.8.0 | Subsonic API version |
c | AnsibleScan | Client name |
fullScan | true/false | Full or incremental scan |
Example URL:
https://server.homelab/rest/startScan?u=joffrey&t=abc123...&s=def456&f=json&v=1.8.0&c=AnsibleScan&fullScan=false
Response (200 OK):
{
"subsonic-response": {
"status": "ok",
"version": "1.16.1",
"type": "navidrome",
"serverVersion": "0.49.3",
"scanStatus": {
"scanning": true,
"count": 1234,
"folderCount": 56,
"lastScan": "2026-01-07T14:30:22Z",
"scanType": "incremental"
}
}
}
Scan Types
Incremental Scan (Default)
What it does:
- Scans only new or modified files
- Checks file modification timestamps
- Fast (seconds to minutes)
- Doesn’t re-read existing files
Use cases:
- Added new albums
- Regular scheduled scans
- Quick library updates
- Daily/weekly automation
Performance:
- Small library (100 albums): ~5-10 seconds
- Medium library (1000 albums): ~30-60 seconds
- Large library (10000+ albums): ~2-5 minutes
Full Scan
What it does:
- Re-scans entire music library
- Re-reads all file metadata
- Updates all database entries
- Slow (minutes to hours depending on library size)
Use cases:
- Library reorganization
- Fixing metadata issues
- Database recovery
- Periodic maintenance
- Initial library setup
Performance:
- Small library (100 albums): ~1-2 minutes
- Medium library (1000 albums): ~10-20 minutes
- Large library (10000+ albums): ~1-2 hours
Scan Status Information
Scan Response Fields
| Field | Description |
|---|---|
scanning | Boolean, true if scan in progress |
count | Number of tracks found/scanned |
folderCount | Number of folders scanned |
lastScan | Timestamp of last completed scan |
scanType | ”incremental” or “full” |
Example Output
TASK [Display scan response]
ok: [localhost] => {
"msg": "Library scan triggered successfully. Scanning: true, Type: incremental"
}
TASK [Display scan statistics]
ok: [localhost] => {
"msg": [
"Tracks found: 1234",
"Folders: 56"
]
}
Monitoring Scan Progress
Via Navidrome Web UI
- Log into Navidrome
- Go to Settings → Activity
- View scan progress in real-time
Via API (Manual Check)
# Get scan status
curl -X GET \
"https://server.homelab/rest/getScanStatus?u=username&t=token&s=salt&f=json&v=1.8.0&c=curl"
# Response shows current scan status
Via Navidrome Logs
# Docker logs
docker logs navidrome -f
# Look for:
# [INFO] Starting library scan...
# [INFO] Scanned 1234 tracks in 56 folders
# [INFO] Scan completed in 45 seconds
Scan Behavior
Multiple Scans
- Only one scan can run at a time
- Starting a new scan while one is running has no effect
- Wait for current scan to complete before starting another
Automatic Scans
Navidrome can be configured to scan automatically:
In Navidrome config:
# Auto-scan on startup
ScanSchedule: "@every 1h" # Scan every hour
# Or disable auto-scan (manual only)
ScanSchedule: ""
This role triggers manual scans regardless of auto-scan settings.
Scan Triggers
Scans are typically triggered when:
- New music files added
- Files modified or deleted
- Metadata updated
- Playlists changed
- Manual trigger (this role)
- Scheduled auto-scan (Navidrome config)
Security Considerations
- Credentials Protected: Username/password in Ansible Vault
- Token Authentication: Password never in URL
- Random Salt: Prevents replay attacks
- HTTPS Required: Uses SSL/TLS for API calls
- No Logging: Credentials not logged by role
- Validate Certs: SSL certificate validation enabled
Tags
This role does not define any tags. Use playbook-level tags if needed:
- hosts: localhost
roles:
- navidrome_scan
tags:
- navidrome
- scan
- music
- library
Notes
- Role runs on localhost (API calls don’t require target host)
become: falserecommended (no root needed for API calls)- Scan is asynchronous (role triggers, doesn’t wait for completion)
- Multiple concurrent scans not supported by Navidrome
- Salt is randomly generated for each run (6 hex characters)
- API version 1.8.0 is standard Subsonic compatibility version
- Client name set to “AnsibleScan” for identification
Troubleshooting
”Authentication failed” error
Cause: Invalid credentials or incorrect token
Solution:
# Verify credentials in vault
ansible-vault view group_vars/all/vault.yml
# Test login via Navidrome web UI
# Open: https://server.homelab
# Test API manually
curl -X GET \
"https://server.homelab/rest/ping?u=username&p=enc:password&f=json&v=1.8.0&c=curl"
“Connection refused” errors
Cause: Navidrome not running or URL incorrect
Solution:
# Test Navidrome accessible
curl -I https://server.homelab
# Check Navidrome running
docker ps | grep navidrome
# Check Navidrome logs
docker logs navidrome
Scan shows “scanning: false” immediately
Cause: Scan already in progress or no changes detected
Solution:
- Wait for current scan to complete
- If incremental scan finds no changes, this is normal
- Try full scan:
navidrome_scan_full: true
No tracks found (count: 0)
Cause: Music library empty or path incorrect
Solution:
# Check Navidrome config
docker exec navidrome cat /data/navidrome.toml
# Look for MusicFolder setting
# Verify path exists and contains music files
# Check file permissions
ls -la /path/to/music/library/
SSL certificate errors
Cause: Self-signed certificate or CA not trusted
Solution: Set validate_certs: false (not recommended):
# In tasks/main.yml, modify uri task:
validate_certs: false
Or install CA certificate on Ansible control node.
Scan takes very long time
Normal for large libraries:
- 10,000+ tracks: 30+ minutes for full scan
- Check Navidrome logs for progress
- Consider incremental scans for regular updates
Testing the Role
Verify Scan Triggered
Check role output:
TASK [Display scan response]
ok: [localhost] => {
"msg": "Library scan triggered successfully. Scanning: true, Type: incremental"
}
Check Navidrome UI:
- Log into Navidrome
- Go to Settings → Activity
- Should show “Scanning library…”
Check via API:
# Get scan status
curl "https://server.homelab/rest/getScanStatus?u=username&p=enc:password&f=json&v=1.8.0&c=curl"
Verify New Music Detected
After scan completes:
- Check Navidrome for new albums
- Search for newly added artists
- Verify track count increased
Performance Considerations
- Network Latency: API call is fast (~1 second)
- Scan Duration: Depends on library size and scan type
- CPU Usage: Navidrome uses CPU during scan (especially full scan)
- Disk I/O: Reading file metadata uses disk I/O
- No Impact: Role execution doesn’t affect scan performance
Best Practices
- Use incremental scans for regular updates (default)
- Schedule scans after adding music (not during playback)
- Full scans monthly for maintenance
- Monitor scan progress in Navidrome UI
- Store credentials in vault (never plaintext)
- Test with small library before large deployments
- Avoid concurrent scans (wait for completion)
- Check Navidrome logs for scan errors
- Document scan schedule (daily, weekly, etc.)
- Combine with music copy tasks (copy, then scan)
Integration Examples
Scan After Music Copy
---
- name: Add Music and Scan
hosts: media_server
become: true
tasks:
- name: Sync music from source
ansible.posix.synchronize:
src: /source/music/
dest: /mnt/music/library/
delete: false
- name: Trigger Navidrome Scan
hosts: localhost
become: false
roles:
- navidrome_scan
Scan After Metadata Update
---
- name: Update Music Metadata
hosts: media_server
become: true
tasks:
- name: Run beets import
ansible.builtin.command:
cmd: beet import -q /mnt/music/new/
chdir: /home/music
- name: Scan Updated Library
hosts: localhost
become: false
vars:
navidrome_scan_full: true # Full scan after metadata changes
roles:
- navidrome_scan
Related Roles
This role is often used with:
- navidrome_playlists_backup: Backup playlists after scan
- Music management roles: Copy/organize music files
- Monitoring roles: Alert on scan failures
Alternative: Manual Scan
If you prefer manual scanning:
Via Navidrome UI:
- Log into Navidrome
- Go to Settings → Activity
- Click “Scan library now”
Via API (curl):
# Generate token
PASSWORD="your_password"
SALT=$(openssl rand -hex 3)
TOKEN=$(echo -n "${PASSWORD}${SALT}" | md5sum | cut -d' ' -f1)
# Trigger scan
curl -X GET \
"https://server.homelab/rest/startScan?u=username&t=$TOKEN&s=$SALT&f=json&v=1.8.0&c=curl&fullScan=false"
License
MIT
Author
Created for homelab infrastructure management.