Migrating an AI Agent from WSL to Docker on Unraid
My AI agent — Hermes — had been running in WSL2 for months. It worked, mostly. The problem was that WSL2 terminals would randomly die. Sometimes the entire distro would vanish overnight. The agent's brain, including its memory, cron jobs, and conversation state, lived in an environment that couldn't be trusted to stay running.
The Problem with WSL2 as Production
WSL2 is fantastic for development. It's a terrible place to run anything that needs to survive a reboot. The filesystem is ephemeral by default, the networking stack is NAT'd behind Windows, and the lifecycle is tied to whatever Windows decides to do with your VM.
For an AI agent that runs 24/7 — processing cron jobs, maintaining Discord bot connections, and keeping conversation state — this was a ticking time bomb. Every time WSL crashed, I'd lose whatever the agent was doing and have to manually restart everything.
The Target: Docker on Unraid
I have an Unraid server (a mini6900hx) already running Docker containers for LanceOS, BizExpenses, and other services. It's stable, has UPS backup, and its filesystem persists across reboots. Moving Hermes there was the obvious call.
The Phased Approach
I didn't want a big-bang migration. Too many things could go wrong. Instead, I broke it into gates:
- Gate A: Establish SSH access from the current environment to Unraid
- Gate B: Create the Unraid directory structure, capture inventory, seed source files
- Gate C: Build Docker image, write compose file, test health checks
- Gate D: Cutover — stop WSL services, start Unraid services, verify
Each gate had an explicit approval step. If something broke, I could roll back to the previous gate without losing anything.
SSH Key Setup
First, I generated a dedicated SSH key for the migration:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_unraid_root \
-C "lhiggins@wsl-to-unraid-root"
Then installed it on Unraid with ssh-copy-id. This gave me passwordless SSH access as root, which I'd need for Docker operations. The key was stored in the Hermes secrets directory for persistence.
Seeding the Source
The Hermes home directory was about 3GB, but most of that was rebuildable (venv, node_modules, .git). I used rsync over SMB to seed the source, excluding the bulk:
rsync -a --info=progress2 --inplace \
--exclude='.git' --exclude='venv' --exclude='node_modules' \
--exclude='__pycache__' --exclude='*.pyc' \
~/.hermes/hermes-agent/ /mnt/unraid/hermes/source/hermes-agent/
The final seed was 386MB across 8,363 files. Much more manageable.
The Backup
Before touching anything, I took a compressed backup of the entire WSL Hermes home:
tar --zstd -cf hermes-wsl-pre-migration-20260518.tar.zst ~/.hermes/
The result: a 1.0GB archive with a SHA256 checksum, stored on Unraid. If everything went sideways, I could restore from this in minutes.
Docker Compose
The final compose file runs two Hermes profiles — a default gateway for the TUI/API, and a Discord-specific gateway. Both share the same home directory mount:
services:
hermes-gateway:
build: ./source/hermes-agent
volumes:
- /mnt/user/appdata/hermes/home:/home/lhiggins
ports:
- "9119:9119"
restart: unless-stopped
hermes-gateway-discord:
build: ./source/hermes-agent
volumes:
- /mnt/user/appdata/hermes/home:/home/lhiggins
restart: unless-stopped
Both containers mount the same persistent home directory, so memory, skills, and config are shared.
The 72-Hour Soak Test
After cutover, I didn't touch anything for 72 hours. I watched the logs, checked cron job execution, verified Discord bot uptime, and monitored memory usage. Everything held. The agent had been running for months on WSL with regular crashes. On Docker/Unraid, it's been up for weeks with zero incidents.
Lessons
- Ephemeral environments are for development, not production. If your service needs to survive reboots, it needs persistent storage.
- Gated migrations beat big bangs. Each rollback point saved me from at least one "oh no" moment.
- Compressed backups are cheap insurance. 1GB of backup storage vs. hours of rebuilding state? Easy choice.
- Docker Compose is the right abstraction. One file describes the entire service topology. Reproducible, versionable, and clear.
I consult on Docker migrations, home server setup, and AI agent infrastructure. Real systems, not theory.
Work with me →