Auditing and Consolidating a Self-Hosted Media Stack
After running a home media server in production for several weeks, storage fragmentation and orphaned files inevitably accumulate. This article documents a comprehensive September 2026 audit to reclaim space, validate architecture integrity, and establish a repeatable maintenance process — with emphasis on hardlink preservation and safe cleanup strategies.
The Initial Problem: Fragmented Mount Paths
The stack initially worked, but the storage layout was not path-clean. Instead of a single unified mount, media was spread across three separate NFS mount points:
/mnt/movies— Radarr import destination/mnt/tv— Sonarr import destination/mnt/downloads— qBittorrent download source
This fragmentation prevented hardlinking between the download directory and the library directories. When Radarr imported a film from /mnt/downloads to /mnt/movies, it had to use cp() instead of link(), doubling disk usage for every single import. Over time, the impact was massive: a 60 GB film imported to Radarr would consume 120 GB total (60 GB in downloads, 60 GB in movies).
The Solution: Unified /data Mount Architecture
The refactored architecture consolidates all media under a single NFS mount exposed consistently to all containers:
On the Host
mount -t nfs 192.168.1.19:/volume1/Media /mnt/media
In Docker Compose
Every download and media-management service uses the same bind mount:
services:
qbittorrent:
volumes:
- /mnt/media:/data
radarr:
volumes:
- /mnt/media:/data
sonarr:
volumes:
- /mnt/media:/data
Inside the Containers
All services see a single unified layout:
/data/downloads → qBittorrent save path
/data/movies → Radarr root folder
/data/series → Sonarr root folder
Why This Matters
Since all three directories are on the same NFS filesystem (same inode table), hardlinks work seamlessly across services. When qBittorrent downloads a 60 GB film to /data/downloads and Radarr imports it to /data/movies, Radarr uses link() instead of cp(). The result: one physical copy, two logical paths, zero additional disk usage.
Audit Results: September 2026
| Component | Initial State | After Audit | Outcome |
|---|---|---|---|
| Radarr orphaned films | ~176 leftover downloads | ~115 properly imported | ~150 GiB reclaimed |
| Sonarr episode orphans | Unknown | 0 true orphans found | Series library confirmed clean |
| qBittorrent Sonarr torrents | 96 total entries | 59 active torrents | 37 inactive torrents removed |
| Empty directories | 11 orphaned | 0 remaining | Filesystem cleaned |
| Hardlink verification | Unconfirmed | 5 active hardlinks verified | Hardlink architecture validated |
Audit Methodology
1. Radarr Orphan Detection
Cross-reference Radarr’s database of imported films with the actual files in /mnt/media/downloads. Mark a file as a safe orphan candidate if:
- It is NOT referenced by any active Radarr entry
- It has
links=1(no hardlink protection) - It is not associated with any active qBittorrent torrent
Result: ~61 films safely deleted, freeing approximately 150 GiB.
2. Sonarr Episode Audit
Similarly audit Sonarr series, checking for episodes that:
- Do not belong to any active Sonarr series entry
- Are not associated with active qBittorrent torrents
- Have
links=1(not hardlink-protected)
Result: 0 orphans found. The Sonarr library was completely clean — all existing episodes were accounted for and properly imported.
3. qBittorrent Inactive Torrent Cleanup
Identify torrents that are safe to remove from qBittorrent without reclaiming significant disk space. Filter by:
category=sonarr(only Sonarr-managed entries)progress=0.0(never started downloading)- State in
{queuedDL, metaDL}(stuck in queue, not downloading)
These torrents were added by Sonarr but never actually began their download. Removing them did not reclaim significant space, but it reduced database clutter and made qBittorrent easier to reason about.
Result: 37 inactive torrents removed. qBittorrent database reduced from 96 to 59 entries.
4. Hardlink Verification
For any imported content, verify that hardlinks are actually in place and protecting the data:
find /mnt/media/series -type f -exec stat -c '%h %n' {} \; | awk '$1 > 1'
This command finds all files in the series directory with a link count greater than 1.
Result: 5 files with links=2 confirmed in /mnt/media/series/Chernobyl/Season 01. These represent hardlinked copies between the download directory and the series import directory. One physical file, two logical paths.
Key Lessons Learned
Container Path Mapping Is Critical
When auditing, never mix container-internal paths with host paths without first validating the mapping. In this case, qBittorrent reported content under /data/downloads, but the host filesystem showed /mnt/media/downloads. A naive audit script could incorrectly label active torrent directories as orphans if it did not account for this mapping.
Always validate the bind mount before trusting any cleanup decision:
docker inspect qbittorrent --format '{{range .Mounts}}{{println .Source " -> " .Destination}}{{end}}'
In this case, the critical mapping was: /mnt/media (host) → /data (container).
Torrent State vs. Physical Content
A torrent in queuedUP state with progress=1.0 is actively seeding completed content that is already hardlinked into your library. Do NOT delete the torrent simply because it is not actively downloading. Removing it stops peer contribution but reclaims zero disk space (the hardlinked files remain in the library). The content is protected by being referenced from both the downloads directory and the library directory.
Hardlinks Change Storage Math
Seeing the same episode in both a download directory and a library directory does not automatically mean the file is duplicated. Check the link count first. If links > 1, the file is hardlinked and deleting either copy while the other exists will reduce the link count but not reclaim disk space until the last link is removed.
Safe Cleanup Requires Conservative Assumptions
When in doubt, keep the file. The cost of keeping extra data is measured in storage; the cost of deleting something irreplaceable is immeasurable. Always cross-reference with at least two sources (Radarr/Sonarr database, qBittorrent content mapping, filesystem link count) before marking a file for deletion.
Recommended Maintenance Schedule
- Monthly: Review Radarr leftovers in
/mnt/media/downloads. Films accumulate quickly as new releases are added. - Quarterly: Verify Sonarr hardlinks and active torrent mappings. Ensure the Docker network and storage paths are still consistent.
- After any container recreation: Validate that the
/mnt/media:/databind mount is still in place and that all services still see a unified filesystem. - Before any major storage operation: Audit empty directories, queued torrents, and save-path consistency to avoid unexpected space issues.
Security Considerations
If you expose the stack to the internet, keep the public surface minimal. Only Jellyfin and Jellyseerr should be externally accessible. qBittorrent, Radarr, Sonarr, and Prowlarr must stay behind local-only access controls, a VPN, or strict reverse-proxy middleware. Exposing these management interfaces publicly creates significant security risks.
Why This Audit Mattered
Running a media stack on a fixed-size storage device requires discipline. Without a repeatable audit process, storage gradually fragments, old downloads accumulate invisibly, and cleanup becomes a risky guessing game. By establishing clear criteria for what is safe to delete, validating hardlink protection, and verifying Docker path mappings, the audit process became trustworthy enough to automate.
The consolidated /data layout is not just cleaner — it makes audits dramatically simpler. All media lives under one filesystem, all services see consistent paths, and hardlink validation is straightforward. Future audits will be faster and safer.
Stack: Jellyfin, Jellyseerr, Radarr, Sonarr, Prowlarr, qBittorrent, Bazarr. Infrastructure: Proxmox LXC, Synology NAS over NFS, Docker Compose. Audit conducted September 2026.

Leave a Reply