Navidrome is not guessing at your library, and it is not corrupting it. It builds albums from the tags inside your files, then keys every album and track to a persistent ID derived from those tags, so a duplicate album means two groups of files disagree about ALBUMARTIST, album name, release date or MusicBrainz ID. Split discs, "Various Artists" fragmentation and missing covers come from the same root cause, one layer down: inconsistent tags, or a filesystem that reports different modification times and paths on every mount. Fix the tags and stabilise the mount, and the scanner stops being the problem.
TL;DR by reader profile
- Solo developer with a 50,000 track library on a home NAS, mounted over SMB: move the music onto a local disk or a stable NFS export before touching tags, because mtime drift makes every scan a full scan and hides whether your tag fixes worked.
- Anyone whose library came from mixed sources over ten years, ripped CDs plus purchased downloads plus old downloads: run MusicBrainz Picard over the worst offenders first, since missing ALBUMARTIST and inconsistent album titles cause the large majority of duplicate album reports.
- Beets user who already has a tidy, scripted library: keep beets as the source of truth and point Navidrome at its output read only, because two tools writing tags to the same files is how you get half-updated albums.
- Docker Compose user running Navidrome next to Jellyfin, Plex or Lidarr on the same folder: decide which application owns tag writes and cover files, since competing artwork conventions are the usual reason embedded art stops showing.
- Anyone running from a laptop, an external USB drive or a rclone mount: disable the watcher and use a scheduled scan, because filesystem notification events do not cross most network and FUSE mounts reliably.
- Anyone about to "just delete the database and rescan": export playlists and back up navidrome.db first, because a rebuild recovers metadata but not play counts, ratings or starred items unless the identity tags are stable.
The central tradeoff is this: strict, MusicBrainz backed tags give you a library that survives rescans and server moves, but they cost hours of upfront cleanup that a loose folder based collection never asks for until the day it breaks.
Table of contents
- Why does Navidrome show the same album twice after a scan?
- How Navidrome decides what an album is: persistent IDs and the grouping tags
- Why missing ALBUMARTIST breaks more than any other single tag
- Do you need MusicBrainz IDs in Navidrome, and what actually breaks without them?
- Why does one album split into CD1 and CD2 in the interface?
- Compilations, Various Artists and soundtracks: four ways they fragment
- Why is cover art missing when the file clearly has an embedded image?
- Why does Navidrome rescan the whole library on an SMB or NFS mount?
- Symlinks, bind mounts and container paths: the anti-patterns that hide half a library
Why does Navidrome show the same album twice after a scan?
Because two sets of files disagree about at least one grouping tag, and Navidrome trusts the tags rather than the folder. It never merges albums by directory name. If ten tracks say ALBUMARTIST=Radiohead and the other two say Radiohead with a trailing space, you get two albums, both named the same, sitting side by side in the interface.
The disagreements that produce duplicates:
-
Album title variants: one track tagged
OK Computerand anotherOK Computer (Remastered)are two different releases as far as the scanner is concerned, and the interface offers no visual hint that only the suffix differs. -
Partial ALBUMARTIST coverage: if a tag editor wrote
ALBUMARTISTon eight of twelve tracks, the untagged four fall back to their ownARTISTvalue, so a guest vocalist on track 9 becomes an album of one. -
Mixed MusicBrainz IDs: files carrying
MUSICBRAINZ_ALBUMIDare keyed by that ID, while files without it are keyed by artist plus title plus release data, so a half tagged album cannot group with itself. -
Release date and version drift: a differing
DATE,ORIGINALDATEor release version field separates two otherwise identical halves of the same rip. - Invisible characters: trailing whitespace, curly versus straight apostrophes and non breaking spaces compare as different strings, which is why the two entries look identical on screen.
Check one before guessing. Run ffprobe -show_entries format_tags -v quiet yourfile.flac on a track from each duplicate and compare the output line by line. In most cases the answer is visible within 30 seconds.
How Navidrome decides what an album is: persistent IDs and the grouping tags
Every album and every track gets a persistent ID, computed by hashing a configurable expression made of tag values. Two files that produce the same album PID are the same album. Two files that do not, are not. That is the whole rule, and once you can read the expression, duplicate reports stop being mysterious.
The mechanics you need to know:
-
The expression lives in configuration:
PID.AlbumandPID.Trackinnavidrome.toml, orND_PID_ALBUMandND_PID_TRACKas environment variables in your Compose file, so you can inspect and change the grouping rule without touching a single audio file. -
A comma concatenates, a pipe falls back:
musicbrainz_albumid|albumartistid,album,albumversion,releasedatemeans "use the MusicBrainz album ID if present, otherwise build a key from album artist, album title, version and release date". - The first non empty field wins: this is why a library with MusicBrainz IDs on 60 percent of files behaves as two libraries under one roof, with two different keying strategies running in parallel.
- Track PIDs hang off album PIDs: the default track expression falls back to the album ID plus disc number, track number and title, so an unstable album PID makes every track ID unstable too.
- Changing the expression is not free: new PIDs mean new rows, and play counts, ratings and starred flags are attached to the old IDs.
Practical consequence: prefer fixing tags over rewriting ND_PID_ALBUM. Loosening the expression to hide a duplicate also merges genuinely distinct releases, such as a 1997 pressing and its 2017 remaster.
Why missing ALBUMARTIST breaks more than any other single tag
Because it is the only tag that tells Navidrome who owns the album, and when it is absent the scanner falls back to the per track ARTIST value. On a solo record that fallback is harmless. On anything with a featured guest, a duet, a remixer or an orchestra credit, it shatters one album into several one track albums, each filed under a different artist in the sidebar.
The tag has a different name in every container, which is exactly why editors write it inconsistently: TPE2 in ID3v2 for MP3, ALBUMARTIST as a Vorbis comment in FLAC and Ogg, and aART in MP4 for AAC and ALAC files.
| File situation | What Navidrome shows | Underlying reason |
|---|---|---|
| ALBUMARTIST on all 12 tracks | 1 album, 12 tracks | Every file produces the same album PID |
| ALBUMARTIST on 10 of 12 tracks | 1 album plus 2 strays | The 2 untagged files key off their own ARTIST |
| No ALBUMARTIST, 4 guest features | 5 albums, same cover | Each distinct ARTIST value becomes its own key |
| No ALBUMARTIST on an 18 track compilation | Up to 18 albums | Every track has a different performer |
| ALBUMARTIST with a trailing space on 1 file | 2 albums, identical names | String comparison is exact, whitespace included |
Fix it in bulk rather than by hand. In MusicBrainz Picard, matched releases write album artist automatically. In Kid3 or Mp3tag, select a folder and set the field once for every file in it. The rule that survives contact with a real library: ARTIST describes the performance, ALBUMARTIST describes the shelf it sits on, and it must be identical across every file in the album.
Do you need MusicBrainz IDs in Navidrome, and what actually breaks without them?
No. A library with clean, consistent text tags works fine. What you cannot do is mix the two approaches inside one album, because the pipe fallback in the album PID expression means tagged files key off a 36 character UUID and untagged files key off artist plus title plus date. Half a release with MUSICBRAINZ_ALBUMID is the single most reliable way to manufacture a duplicate that no amount of retyping the album name will fix.
What the IDs buy you once coverage is complete:
-
Immunity to text edits: rename an album from
Vol. 1toVolume 1and the grouping survives, because the key is the UUID and not the string you just changed. -
Artist disambiguation: three different bands called Nirvana get three
MUSICBRAINZ_ARTISTIDvalues, so they stop collapsing into one crowded artist page. - Correct external lookups: artist images, biographies and ListenBrainz submissions resolve against a canonical entity rather than a fuzzy name match.
-
Reliable re tagging later: with IDs embedded,
beet mbsyncor a Picard reload updates metadata from upstream without you re matching anything by hand. - A stable identity across servers: move the library to a new machine, rebuild the database, and the albums come back grouped the same way.
The cost is real. Matching a large collection in Picard is manual work on anything ambiguous: bootlegs, live recordings, regional pressings and unofficial releases often have several candidate releases, or none. A pragmatic split is to tag the 20 percent of albums you actually play, leave the rest on text tags, and never mix within one album folder.
Why does one album split into CD1 and CD2 in the interface?
The folder layout is not the problem. Navidrome will happily assemble a 2 disc, 24 track release from Album/CD1 and Album/CD2, because it groups by tags and ignores directory structure. The split happens when the ripper baked the disc number into the album title, or left the disc tag empty.
The four causes, in the order you should check them:
-
Disc suffixes inside the ALBUM tag: files reading
The Wall (Disc 1)andThe Wall (Disc 2)produce two different album keys, and the sidebar shows two covers with almost identical names. Strip the suffix so both discs carry the exact stringThe Wall. -
Missing DISCNUMBER: the tag is
DISCNUMBERin FLAC and Ogg,TPOSin ID3v2 and thediskatom in MP4. Without it, disc 2 track 1 and disc 1 track 1 both claim position 1, so the album lists two tracks numbered 1 and the play order goes wrong. -
Release version or date drift between discs: if one folder was tagged in a later session with a different
DATEor release version, the grouping expression sees two releases even though the album titles match. -
Per disc MusicBrainz IDs: some releases are modelled as separate media within one release, and a mismatched
MUSICBRAINZ_ALBUMIDon one folder keeps the halves apart permanently.
The bulk fix is a regular expression action in Mp3tag or Kid3 over the parent folder: move the trailing (Disc N) or CDN out of the album field and into the disc number field. Then set DISCTOTAL to 2 on all 24 files so the interface labels the discs correctly.
Compilations, Various Artists and soundtracks: four ways they fragment
A 40 track compilation is the worst case for a tag driven scanner: every track has a different performer, so anything less than perfectly consistent album level tags produces dozens of one track albums. Four distinct failures account for nearly all of it.
-
The compilation flag is missing: the tag is
COMPILATION=1as a Vorbis comment,TCMPin ID3v2 and thecpilatom in MP4. Set it on every file of the release. Without it, nothing marks the album as a multi artist release, and the scanner has only the per track artist to work from. -
Various Artists is spelled four different ways:
Various,Various Artists,VAandV/Aare four separate album artists and therefore four separate albums. Pick the exact stringVarious Artists, which is what MusicBrainz Picard writes, and apply it to all 40 files without exception. -
Per track ALBUMARTIST was written by a helpful editor: some tools copy
ARTISTintoALBUMARTISTwhen the field is blank, so track 7 becomes an album by that one performer. This is silent, and it happens to files you never opened in the editor yourself. - Soundtracks credited to the composer on some tracks only: a film score with 6 licensed songs mixed into 30 cues ends up half credited to the composer and half to the featured bands, splitting one release into two or more.
Verify the whole folder in one pass rather than spot checking. Run ffprobe -show_entries format_tags -v quiet over each file and confirm that album artist, album title and compilation flag are byte identical across all of them.
Why is cover art missing when the file clearly has an embedded image?
Because Navidrome looks for artwork in a configured order, and stops at the first hit. That order is set by CoverArtPriority, or ND_COVERARTPRIORITY in your Compose file, and the default puts embedded art first, then cover.*, folder.* and front.* files in the album directory. If a stale, low resolution folder.jpg outranks your new embedded image under a modified priority string, the old picture wins and nothing looks broken in the logs.
The causes worth checking, in order of how often they turn out to be the real one:
-
The cache folder is not writable: Navidrome resizes artwork and stores it under the cache directory. If the container runs as UID 1000 and the volume is owned by root, extraction silently produces nothing. Run
ls -lnon the cache path and confirm the owner matches the user in your Compose file. - Art is embedded in one track only: the album cover comes from the files themselves, so tagging track 1 and leaving the other 11 bare gives inconsistent results in track level views.
-
Filename case and spelling:
Folder.jpg,AlbumArt.jpgandcover.jpegare not the same as the patterns in your priority list. Standardise on lowercasecover.jpgin every album directory. - The image is not a format the extractor handles: WebP and progressive or CMYK JPEG files embedded by a third party tool are common failures. Re embed as a baseline JPEG or PNG.
- The browser cached the placeholder: after fixing tags, hard reload the page before concluding the fix failed.
Change one variable at a time, then rescan a single album folder rather than the whole library.
Why does Navidrome rescan the whole library on an SMB or NFS mount?
A quick scan compares each directory's modification time against the timestamp of the last scan. Anything newer gets re read. Network filesystems break that comparison in two ways: they report modification times with different precision or a different timezone offset than the local disk, and they can change the reported value on remount. When every folder looks newer than the last scan, every folder is rescanned, and a 50,000 track library never gets a fast pass.
| Mount type | mtime stability across remounts | Practical consequence |
|---|---|---|
| Local ext4 or XFS | Stable to the nanosecond | Quick scans stay quick, watcher works |
| NFSv4 with synced clocks | Stable if server and client run NTP | Reliable, watcher events unreliable |
| SMB3 via cifs | Depends on mount options and server | Frequent full rescans, timestamp drift |
| rclone or other FUSE mount | Often synthesised or coarse | Repeated full rescans, high latency per file |
Diagnose it before changing anything. Run stat -c '%y %n' /music/SomeAlbum from inside the container, unmount and remount the share, then run the identical command again. If the two values differ, the mount is the cause and no amount of tag editing will help.
The fixes that hold, in order of preference:
- Move the library to a local disk: the only option that removes the class of problem entirely.
-
Pin the SMB mount options: set
vers=3.0and keepnoserverinoout of your fstab line, since it also destabilises inode numbers. - Run NTP on both ends: a clock skew of a few seconds, or a 3600 second daylight saving shift, is enough to invalidate the comparison.
- Schedule scans instead of fighting the watcher: covered next, but the mount is the thing to fix first.
Symlinks, bind mounts and container paths: the anti-patterns that hide half a library
Navidrome can only index what it can see and read from inside its own namespace. In Docker that namespace is whatever you bind mounted, so a path that works perfectly in your host shell can be empty, dangling or unreadable to the process doing the scanning. The symptom is not an error. It is an album count that is 8,000 tracks short of what you expected.
-
Symlinks that resolve outside the bind mount: if
/mnt/music/rockis a link to/mnt/tank/rockon the host and you only mounted/mnt/music, the container sees a broken link. Mount the real target as well, or replace the symlink with a second bind mount in your Compose file. -
Directories the container user cannot traverse: a folder with mode 700 owned by root is invisible to a process running as UID 1000, and the scan skips it without stopping. Verify with
docker exec -u 1000 navidrome ls /music/Artistrather than trusting your own shell. -
The data folder living inside the music folder: if
navidrome.dband the cache sit under the scanned tree, the scanner walks its own working files. Keep music at/musicand state at/data, as two separate volumes. -
Renaming the mount point between deployments: switching from
/musicto/media/musicchanges every stored path, so the next scan re imports the entire library even though not one audio file changed. -
Folders you never wanted indexed: drop a
.ndignorefile into a directory to exclude it and its children, which is the clean way to skip incoming downloads or a lossless archive.
Top comments (0)