Tower: Leaving Unraid After 10 Years
ActiveStorage infrastructure — Debian, OMV, mergerfs, SnapRAID, Docker Compose
Migrating a 150 TB Unraid server to Debian, OpenMediaVault, mergerfs, SnapRAID, and Docker Compose without moving the data.
Tower has been my main storage server for about a decade.
For most of that time it ran Unraid, and honestly, Unraid served me really well. I started using it when I wanted a big storage server without having to build every piece of the storage stack myself. It made adding disks easy, gave me Docker without much hassle, and generally let me spend my time using the server instead of constantly administering it.
But Tower changed a lot over those ten years.
What started as a NAS with some Docker containers slowly turned into the center of a ridiculous amount of stuff. It now stores around 150 TB of data, runs more than 60 containers, hosts applications I've written myself, runs parts of my business infrastructure, handles media, home automation, backups, and a bunch of other things I've accumulated over the years.
Somewhere along the way, I think I outgrew Unraid.
I don't mean that as a knock against Unraid. I would still recommend it to plenty of people. The bigger change was me. I became much more comfortable with Linux, Docker, networking, storage, and managing servers directly. Eventually I found myself working around Unraid's abstractions more often than I was benefiting from them.
Then I upgraded Tower to Unraid 7.3.2.
The upgrade was not supposed to contain breaking changes.
My server disagreed.
After the upgrade, shfs would no longer start correctly. /mnt/user disappeared. Docker couldn't find its image. The WebGUI stopped working. The array itself and the XFS filesystems were still there, but most of the things that made Tower actually useful were effectively dead.
SSH still worked.
That turned out to be important.
Initially the obvious plan was to repair Unraid. But while digging through the failure I realized I was about to spend a considerable amount of time rebuilding an environment I'd already been thinking about leaving.
So the question changed.
Instead of asking how to fix Unraid, I started asking whether I could remove Unraid without moving 150 TB of data.
Turns out, yes.
The important thing about an Unraid array is that the individual data disks are still normal filesystems. Mine were XFS.
That meant the data wasn't trapped inside some giant proprietary storage format. If I could identify every disk reliably and mount the existing XFS filesystems without changing them, I could theoretically build a completely different storage stack around the data already sitting there.
So that's what I did.
Tower now runs:
- Debian 13
- OpenMediaVault 8
- Docker Engine and normal Docker Compose
- mergerfs
- SnapRAID
- XFS
The mental model is intentionally boring:
Debian = operating systemOMV = NAS administrationCompose = applicationsmergerfs = unified namespaceXFS = actual filesSnapRAID = parity
There isn't really anything magical holding it together anymore.
And that's exactly what I wanted.
The best part of this migration is that the actual data never had to move.
Tower has 15 existing XFS data disks. Debian mounts those individually as:
/srv/tower/disks/data01.../srv/tower/disks/data15
mergerfs combines them into:
/srv/tower/pool
Applications get the familiar unified filesystem they expect, while the actual files remain on ordinary XFS disks.
I also kept compatibility paths for applications that historically expected Unraid locations:
/mnt/user/mnt/cache/mnt/cache_ingest
Those are now just controlled bind mounts pointing at the new storage layout.
That meant I didn't have to simultaneously rewrite every application just because the underlying OS changed.
One thing I absolutely did not want was a pile of /dev/sdX assumptions holding this together. Disk identity is verified using persistent serial, WWN, and filesystem UUID information.
The storage startup is also intentionally fail-closed. If the disks Tower expects aren't actually present and mounted where they're supposed to be, Docker doesn't get to cheerfully start applications against empty directories.
I'd rather have a server refuse to start than discover later that an application has been writing into the wrong filesystem.
Unraid's parity obviously couldn't come with me.
The old parity disk was reformatted and is now the parity disk for SnapRAID.
SnapRAID is a pretty natural fit for this server because most of Tower's large data is relatively static. I still get independently readable XFS disks while adding parity protection across them.
The configuration has:
- 15 data disks
- 1 parity disk
- 3 distributed SnapRAID content files
- storage identity and readiness checks
- SMART safety checks
- shared locking
- no automatic repair behavior
At the time I'm writing this, the initial parity build is still grinding through the array. I'm deliberately leaving the machine alone until that finishes.
Once it does, Tower will run scheduled nightly syncs and partial weekly scrubs.
No clever automatic snapraid fix jobs. If SnapRAID thinks something is wrong, I want it to tell me. I'll decide what happens next.
One of my bigger goals was getting every application into normal, boring Docker Compose.
Over the years Tower had accumulated more than 60 running containers. Some were already reasonably well defined. Others had years of Unraid history hiding behind them.
Everything currently running in production is now backed by a Compose project.
The authoritative definitions live in:
/srv/tower/compose/stacks/
I also created a tower-infra repository so the configuration itself is versioned instead of living exclusively in a NAS GUI.
I installed Dockge because having a visual way to see and manage Compose stacks is useful, but Dockge isn't allowed to become the source of truth. It indexes the existing repo-backed projects.
The files win.
Recreating containers turned out to be one of the more interesting parts of this project.
My Sweetness & Light website was a particularly good example.
It runs an older OpenCart installation inside a custom Docker image. The original upstream dependency used to build that image no longer exists.
Fortunately, the exact production image was still sitting inside the old Unraid docker.img.
So I recovered it.
Then I discovered something worse.
At some point in the site's history, two important production fixes had been made directly inside the running container:
- an OpenCart storage symlink
- nginx SEO URL routing
They weren't represented in the Docker configuration at all.
The container worked because that particular container had been alive long enough to remember how to work.
That's not exactly the kind of infrastructure documentation I aspire to.
Those fixes are now declarative. The Compose project verifies the storage layout before startup, validates the nginx configuration, and runs application/SEO smoke tests.
I can destroy the container, recreate it from scratch, and the website still works.
That was a pretty satisfying test.
Unraid also had about 94 User Scripts accumulated over the years.
I absolutely did not turn 94 scripts back on.
Every one was inventoried and classified as something to migrate, replace, retire, or leave manual-only.
The useful scheduled jobs became normal scheduled jobs under OMV. Old boot hacks and obsolete workarounds were retired. Potentially destructive maintenance scripts require explicit confirmation.
I did the same thing with backups.
Tower's existing media backup to my TrueNAS server was rebuilt around an explicit inventory and media manifests. Tower remains authoritative, TrueNAS remains the recovery copy, and deletion is controlled rather than blindly mirrored.
My business website also got proper application-aware backups of both its database and application state, with validation and retention.
This migration ended up removing a surprising amount of infrastructure debt I didn't realize I had.
I wanted the underlying server to be conventional Linux, but I also don't want to SSH into it just to answer every simple question.
So Tower has a small private management layer:
- OpenMediaVault for NAS administration
- Homarr as an admin dashboard
- Dockge for Compose visibility
- FileBrowser Quantum for read-only browsing of
/mnt/user - a restricted Docker socket proxy
- a small read-only SnapRAID status service
None of those components owns the architecture.
If Homarr disappears tomorrow, Tower doesn't care.
If Dockge disappears, docker compose still works.
If OMV disappeared, underneath it is still Debian, normal mounts, XFS, mergerfs, SnapRAID, and configuration files I can actually read.
That separation was one of the main things I wanted out of this project.
For me, yes.
But I don't think the lesson here is that everyone running Unraid should replace it with Debian.
Unraid did exactly what I needed it to do for a very long time. If anything, getting nearly ten years out of a server platform while my needs changed this dramatically is a pretty good run.
I just reached the point where I wanted fewer platform-specific abstractions and more direct control over the machine.
The 7.3.2 failure forced the issue.
It was frustrating, but it also gave me the excuse to finally build Tower the way I wanted it.
And somehow the part I was most worried about, moving roughly 150 TB of data, turned into the part I didn't have to do at all.
Not much.
The initial SnapRAID parity build needs to finish. After that I'll validate it, enable the scheduled sync and scrub jobs, and finally do a controlled reboot to make sure the entire machine comes back exactly the way it's supposed to.
Then I'll stop screwing with it.
Hopefully.
The whole point of this project wasn't to build the world's most elaborate homelab storage architecture.
It was the opposite.
After ten years of accumulated history, I wanted Tower to become understandable again.
Standard Linux. Normal filesystems. Normal Compose files. Explicit configuration. Things that fail when they're supposed to fail.
I wanted Tower boring again.
And I think I'm finally there.