qBittorrent: migrating seeding torrents from Windows to Linux
qBittorrent: migrating seeding torrents from Windows to Linux (Docker)
A guide for moving a large set of seeding torrents from a qBittorrent client on Windows to qBittorrent in Docker on Linux — without breaking the seeding (which matters for private trackers like TorrentLeech and their Hit-and-Run rules).
Done 2026-06-29: 193 torrents (movies, anime, TV, PC games, OS ISOs) migrated from a Windows server to Docker qBittorrent on the arr-server. All 193 are seeding, verified to survive a reboot.
Prerequisites
- The source files already live on the NAS (the same physical files the Linux client will seed).
- Both the Windows and Linux qBittorrent run a standard release (not a beta or fork) — otherwise the tracker may ban the client. qBittorrent 5.2.2 is fine for TorrentLeech.
- The torrents have already met the tracker's minimum seed time (if not: migrate anyway, but be aware of the Hit-and-Run risk).
NAS layout (NFS on the arr-server)
| NAS | Export | Mount point |
|---|---|---|
| NAS01 | /volume1/Movies |
/mnt/media/movies |
| NAS01 | /volume1/Apps |
/mnt/media/apps |
| NAS02 | /volume1/TV |
/mnt/media/tv |
| NAS02 | /volume1/Anime |
/mnt/media/anime |
| NAS02 | /volume1/Games-PC |
/mnt/media/games-pc |
Step 1 — Export from Windows
The .torrent and .fastresume files live in:
C:\Users\<user>\AppData\Local\qBittorrent\BT_backup\
Copy the entire folder (both the .torrent AND the .fastresume files) to somewhere Linux can reach, e.g. a temp folder on the NAS.
Note: always keep an untouched copy as a "pristine source" — never touch it, always recreate from it.
Step 2 — Docker volumes
The qBittorrent container must see the files at the same path the .fastresume points to. Add the NAS mounts to docker-compose.yml:
qbittorrent:
volumes:
- ./config/qbittorrent:/config
- /mnt/media/movies:/mnt/media/movies
- /mnt/media/tv:/mnt/media/tv
# ...and so on for each share
Step 3 — Rewrite the paths in .fastresume
.fastresume is bencode-encoded (binary, with length prefixes). A plain sed will NOT work — you have to decode, swap the fields, and re-encode. Two fields hold paths:
save_path(libtorrent's — the one that actually counts)qBt-savePath(qBittorrent's own)
Mapping rule (Windows → Linux), prefix-preserving:
| Windows save_path | Linux |
|---|---|
//nas01/Movies (movie in a subfolder) |
/mnt/media/movies |
//nas01/Movies/<movie> (file directly) |
/mnt/media/movies/<movie> |
//nas02/Anime/<x> |
/mnt/media/anime/<x> |
//nas02/TV/<x> |
/mnt/media/tv/<x> |
Important: don't blanket-swap everything to the same folder. If the
save_pathincludes the movie name, that means the content is the file directly — in which case the subfolder has to be preserved.
The Python script uses its own bencode decode/encode with a safety check: encode(decode(file)) == file must be byte-identical before anything is written (otherwise the encoder isn't canonical → abort). Stop qBittorrent before editing the files (it overwrites .fastresume on shutdown).
Step 4 — Load them in and recheck
- Copy the
.torrentfiles + the rewritten.fastresumefiles into the Linux qBittorrent'sBT_backup. docker compose up -d qbittorrent(recreate if volumes changed).- qBittorrent rechecks and starts seeding. Torrents that find their files go to
stalledUP/uploading.
Common errors:
missingFiles= qBittorrent can't find the files → wrong path, a missing docker volume, or a folder name that doesn't match (e.g. a dedup that renamed it).- Trailing space in a folder name: Windows allows/strips trailing spaces differently from Linux. Fix it qBittorrent-native with the
torrents/renameFolderAPI (do NOT rename the folder on disk — Radarr/Jellyfin expect it without the space).
Step 5 — Turn off the seeding queue
For a seedbox that should seed everything forever: turn off queueing so all torrents announce at once.
POST /api/v2/app/setPreferences json={"queueing_enabled":false}
Otherwise max_active_uploads/max_active_torrents limit how many actually seed.
Step 6 — Make it reboot-safe (critical!)
An fstab entry with _netdev survives a reboot — but Docker doesn't wait for the NFS mounts by default. If Docker starts qBittorrent before NFS is ready, empty folders get bind-mounted → every torrent becomes missingFiles.
Fix — a systemd drop-in at /etc/systemd/system/docker.service.d/wait-for-nfs.conf:
[Unit]
After=remote-fs.target
Wants=remote-fs.target
Then systemctl daemon-reload. This orders Docker after all _netdev mounts are ready.
Verify with an actual reboot: after restarting, all mounts should be active, qBittorrent up, and every torrent seeding (0 missingFiles).
Verification commands
# Mounts active?
findmnt -t nfs4 -o TARGET,SOURCE
# fstab valid?
sudo findmnt --verify
# Can qBittorrent see the files?
docker exec qbittorrent ls /mnt/media/movies
# Torrent states (via the API, after getting a login cookie)
curl -s -b cookie 'http://localhost:8080/api/v2/torrents/info' \
| grep -o '"state":"[^"]*"' | sort | uniq -c
# Tracker announcing? (status 2 = working)
curl -s -b cookie 'http://localhost:8080/api/v2/torrents/trackers?hash=<HASH>'