This guide covers moving existing client state into TorrentNG. The migration goal is the import side of universal compatibility: preserve the torrent metadata, paths, labels/categories/tags, counters, file priorities, and resume state that other clients have accumulated wherever the source format makes that recoverable.
TorrentNG also has a reverse export library path in rt_migrate::export for
anti-lock-in workflows. It projects TorrentNG-client DB rows, persisted
.torrent blobs, and TorrentNG-client fastresume state back into qBittorrent/Deluge libtorrent,
Transmission, rTorrent, uTorrent/BitTorrent Classic, BiglyBT/Vuze, or generic
.torrent + manifest layouts. Export plans report whether each torrent is
recheck-free, complete-only, metadata-only, or torrent-only for the chosen
target.
Current migration coverage includes rTorrent, qBittorrent, Transmission, Deluge,
uTorrent/BitTorrent Classic, BiglyBT/Vuze, Tixati, and generic .torrent
directories, with exact status tracked in
CLIENT_COMPATIBILITY_MATRICES.md. For the
broader TorrentNG client overview and client-swap workflow, see
ENGINE_REWRITE.md.
Migration reports use two different vocabularies depending on direction. They measure different things at different times, and conflating them is the most common way to misread a dry-run report:
| Direction | Vocabulary | What it measures |
|---|---|---|
Importing in (torrentngd migrate) |
trusted / hints / metadata-only / none |
How much piece state could be decoded from the source client at scan time |
Exporting out (torrentngd export) |
recheck-free / complete-only / metadata-only / torrent-only |
How much of TorrentNG-client state survives being written into the target client's format |
A torrent that imports as hints — the normal label for anything mid-download,
see below — is not a lower grade of
imported data. Once it's in TorrentNG-client state, it can still export
later as recheck-free: hints only describes what the scanner could
confirm about the source at import time, not what survived the import.
For TorrentNG-client deployments, torrentngd migrate is the one-shot import
path. It scans a source client's state directory read-only, prints a dry-run
report, and (with --apply) writes TorrentNG-client DB rows and compatible fast-resume
state together so complete torrents resume seeding without a full recheck.
torrentngd migrate --source <SRC> --from <DIR> [OPTIONS]--source—rtorrent,qbittorrent,transmission,deluge,utorrent,biglybt,tixati, orgeneric--from— the source client state directory (never modified)--apply— perform the import; omit for a dry-run report (the default)--policy— fast-resume trust:verify,trust-hints(default), ortrust-all.trust-hintsonly trusts complete state when the data files are present at the expected sizes; anything else falls back to verification. If the source client looks like it crashed, the policy is lowered toverifyautomatically (see Crash detection).--trust-unclean-source— keep--policyeven when the source looks crashed--allow-running-source— import even though the source client appears to be running (its state may be mid-write; refused by default)--remap OLD=NEW— rewrite a save-path prefix (repeatable), e.g.--remap /downloads=/datawhen data moved into a container--default-save-path DIR— fallback save path when the source recorded none--report FILE— also write the markdown dry-run report toFILE--config FILE— config file (elseTORRENTNGD_CONFIG/ defaults); this determines the TorrentNG-client DB and fast-resume target locations--yes— skip the confirmation prompt with--apply
A resume bitfield saved just before a crash can claim data that never reached
disk; importing it under a trusting policy reproduces the failure described in
CRASH_SAFETY.md. Before applying, torrentngd migrate checks
the source's shutdown state:
| Source | Signal | Result |
|---|---|---|
| rTorrent | rtorrent.lock (host:+pid) in the session directory |
no lock: clean. Lock and a live rtorrent process: running, refused unless --allow-running-source. Lock but no such process (or another host, or unreadable): crashed |
| qBittorrent | lockfile (Qt QLockFile: pid, app, host) in the state directory or up to two parents |
lock found: same rules as rTorrent. Not found: unknown, not clean |
| Deluge | deluged.pid (pid;port) in the state directory or its parent |
same as qBittorrent |
| any other source, or a non-standard location | --source-lock FILE |
a lock or pid file you name: found and stale is a crash, found and live is running, missing means the client exited cleanly |
every other source without --source-lock |
none TorrentNG can read | unknown; nothing is assumed |
The qBittorrent and Deluge file names, formats and locations come from those
clients' documented behavior and were not verified against the real programs; if
yours keeps its lock elsewhere, pass it with --source-lock.
A lock is judged by its process: gone means the last run did not finish; alive
and named like the client means it is running now; alive but named like an
unrelated program means the pid was reused (crash); a scripting-language host
such as python counts as possibly the client. Process checks use /proc on
Linux, kill and ps on macOS and FreeBSD, and OpenProcess on Windows.
For a crashed source the policy becomes verify, so every imported torrent is
hash-checked before it is trusted; the dry-run prints this, and
--trust-unclean-source overrides it. For sources with no signal, after any
crash of that client pass --policy verify yourself.
The dry-run report and the post-apply summary both break torrents down into
trusted / hints / metadata-only / none so you can see how much state will
avoid a recheck before committing. Always run the dry-run first. The
TorrentNG-client DB and fast-resume directory are taken from the resolved
config, so point --config at the same config torrentngd uses, and back up
the TorrentNG-client DB (see
BACKUP_RESTORE.md) before --apply.
The four buckets describe what was decoded at scan time, not the final recheck decision:
- trusted — piece state decoded and every backing file matched the expected size on disk. Resumes with no recheck.
- hints — piece state decoded, but not all files validated by size at
scan time. This is the normal label for an in-progress download: the
partially-written file can never size-match the full torrent length, so the
torrent is reported as
hintseven though its completed-piece map was fully imported. - metadata-only — no decodable piece state (e.g. an incomplete rTorrent torrent, or Tixati). Save path, labels, and counters import; content is rechecked.
- none — only
.torrentmetadata was importable.
A hints partial still skips the full recheck under the default
trust-hints policy. The TorrentNG client's startup guard invalidates only pieces
whose backing files changed since import — not pieces that are merely
incomplete — so a half-finished qBittorrent/Deluge download keeps its
completed pieces and only fetches the rest. The label is a scan-time artifact
of size-only file validation, not a downgrade of the imported piece state.
Only --policy verify forces a full rehash of decoded state; trust-all
trusts it even if files are absent. For this guard to hold, import after
the data is in its final location (use --remap); moving or re-copying files
after --apply changes their size/mtime and reverts the affected pieces to
verification.
- rTorrent session directory (
.torrentfiles and.rtorrentresume files) - Save paths from resume state
- Labels/categories stored in
d.custom1 - Tracker lists
- Upload/download stats from resume state
- Completed/seeding state (no forced recheck for complete torrents)
- ruTorrent per-plugin settings (RSS rules, ratio groups, labels UI state)
- ruTorrent views and saved searches
- Plugin-stored custom metadata beyond
d.custom1–5
For TorrentNG-client deployments, import the rTorrent session directly with
torrentngd migrate. Stop rTorrent (or work
from a copy of the session directory) so resume files are not mid-write, then:
# 1. Dry-run: read-only, shows trusted/hints/metadata-only/none counts
torrentngd migrate --source rtorrent \
--from ~/.rtorrent-session \
--report /tmp/rtorrent-migration.md
# 2. Back up the TorrentNG-client DB (see BACKUP_RESTORE.md), then apply
torrentngd migrate --source rtorrent \
--from ~/.rtorrent-session \
--remap /old/downloads=/data \
--applyComplete torrents whose data files are present at the expected sizes import as
trusted and resume seeding with no full recheck. Incomplete downloads and any
torrent whose files are missing or resized fall back to verification — rTorrent
partial resume state is not decoded, so in-progress downloads will recheck.
Use --remap if the download paths differ in the new deployment. The rTorrent
session directory is read only; nothing is written to it.
Use this path when keeping rTorrent as the transfer client (integration or comparison).
Step 1: Run the diagnostic first
./scripts/healthcheck.sh /path/to/rtorrent.sockFix any issues it reports before migrating.
Step 2: Back up everything
# rTorrent session
cp -r ~/.rtorrent-session ~/rtorrent-session.bak
# ruTorrent settings
cp -r /var/www/rutorrent/conf ~/rutorrent-conf.bak
cp -r /var/www/rutorrent/share ~/rutorrent-share.bakStep 3: Note your current config
cat ~/.rtorrent.rcKey values to record:
session.path— session directorydirectory.default.set— default download pathnetwork.port_range.set— listen portscgi_localorscgi_port— RPC socket
Step 4: Deploy the Phase 1 bundle
docker compose -f deploy/docker/compose.phase1.yml up --buildMap your existing session and data directories to the container volumes:
# In your compose override:
volumes:
- /your/existing/rtorrent-session:/session
- /your/existing/downloads:/data
- /your/existing/config:/configPlace any .rtorrent.rc overrides in /config/rtorrent.rc.
More deployment details are in DEPLOYMENT.md.
Step 5: Verify
After starting the new container:
- Check the ruTorrent torrent list — all torrents should appear in their previous state
- Verify no torrents have started unnecessary rechecks
- Check that tracker announces are working
- Confirm save paths are correct
- Complete torrents that were seeding will resume seeding. They are not forced to recheck.
- Partial downloads resume from where they left off (rTorrent resume state is preserved).
- If a torrent shows "hash check" on startup, the resume file may be missing or corrupt — this is from rTorrent's own resume logic, not a migration artifact.
BT_backup/directory (.torrentfiles and.fastresumefiles)- Categories
- Tags
- Save paths
- Torrent states (paused/seeding/downloading)
- Tracker lists
Step 1: Locate qBittorrent data
Common locations:
- Linux:
~/.local/share/data/qBittorrent/BT_backup/ - Docker: inside the container at the above path
Step 2: Copy BT_backup to a staging directory
cp -r ~/.local/share/data/qBittorrent/BT_backup /tmp/qbt-migrationStep 3: Run dry-run scan and TorrentNG-client import
torrentngd migrate --source qbittorrent --from /tmp/qbt-migration --report /tmp/qbt.md
torrentngd migrate --source qbittorrent --from /tmp/qbt-migration --applyThis pairs .torrent files with .fastresume sidecars and imports save path,
category, tags, trackers, completion state, file rows, transfer counters, and
ratio. qBittorrent's libtorrent-style resume data carries piece state, so
complete and partial torrents both import their progress and avoid a full
recheck under the default trust-hints policy when the data files are present.
Manual fallback remains available: load each .torrent through the TorrentNG
API, pointing at the existing file path, then let the TorrentNG-client recheck job verify
and resume without downloading.
- Torrent files from
~/.config/transmission/torrents/ - Resume data from
~/.config/transmission/resume/ - Download directories
Run torrentngd migrate against the
Transmission config directory (the parent of torrents/ and resume/):
torrentngd migrate --source transmission --from ~/.config/transmission --report /tmp/tr.md
torrentngd migrate --source transmission --from ~/.config/transmission --applyTransmission progress bitfields are decoded, so completed and partial torrents avoid a full recheck when their data is present. Manual fallback remains:
- Export torrent files from Transmission (right-click → "Export .torrent")
- Add each via the TorrentNG API, pointing to the existing download path
- Run a TorrentNG-client recheck job so verified pieces resume without re-downloading
Pass the matching --source (deluge, utorrent, biglybt, or tixati)
to torrentngd migrate with the client's
state/config directory as --from. The scanner imports .torrent metadata,
save paths, labels/categories, transfer
counters, lifecycle timestamps, active/paused state, per-file wanted/priority
state, per-file completed bytes, trackers, generic tracker timing/scrape state,
and any compatible fast-resume piece state it can decode.
Current fast-resume confidence:
- rTorrent: complete
.rtorrentsidecars can synthesize seed piece state when matching files are present; incomplete or unsupported sidecars fall back to verification. - Deluge: libtorrent-style resume data is imported like qBittorrent.
- uTorrent/BitTorrent classic: aggregate
resume.datentries keyed by raw, hex, or base32 info-hash are imported. The per-torrenthave/bitfieldpiece map is decoded, so completed pieces resume without a full recheck under the defaulttrust-hintspolicy — the same as qBittorrent/Deluge. - BiglyBT/Vuze: aggregate
downloads.configentries keyed by info-hash are imported, including nested resume bitfields when present. - Tixati:
.torrentmetadata, save paths, labels, and counters import, but progress state is an undocumented proprietary binary format and is verification-first by design — Tixati torrents are rechecked rather than risk trusting guessed piece state. This is a deliberate safety choice; lifting it needs a real Tixati state corpus and a strict decoder.
Use dry-run output before applying any import. The source directories are read only. Dry-run reports include trusted, hints, metadata-only, and none counts so operators can see how much state will avoid a full recheck. Low-confidence or unsupported resume data is downgraded to normal verification rather than trusted as complete. Piece-state length mismatches are reported and normalized to the torrent piece count before TorrentNG-client fast-resume state is written.
If your download directories are in different locations in the new setup, you need to remap paths before adding torrents.
# Example: old path /downloads, new path /data
# Set in rtorrent.rc or via API after adding:
# d.directory.set=<new_path>/<torrent_name>The TorrentNG client and compatible-client service both support per-torrent save path updates. Bulk moves should use the TorrentNG storage planning flow so conflicts, capacity, copy/rename mode, rollback, and destructive delete approval are visible before data moves.
Pass remaps to torrentngd migrate with repeatable --remap OLD=NEW flags
(e.g. --remap /downloads=/data). Each remap is applied both to file-hint
validation — so trusted fast-resume state is still recognized after moving data
into a container — and to the TorrentNG-client DB save path written during
import. The
longest matching prefix wins when multiple remaps apply.
torrentngd migrate --apply is the combined TorrentNG-client import path: DB rows and
compatible fast-resume state are written together under one audited summary.
Reverse migration is a first-class, no-lock-in path. torrentngd export
reads TorrentNG-client state read-only (the session DB, persisted .torrent
blobs, and fast-resume state) and writes another client's on-disk layout so
you can resume seeding elsewhere — without a full recheck wherever the target
format can carry piece state.
torrentngd export --format <FMT> --to <DIR> [OPTIONS]--format—generic,libtorrent(qBittorrent/Deluge),transmission,rtorrent,utorrent, orbiglybt--to— output directory for the exported layout--apply— write the export; omit for a dry-run report (the default)--report FILE— also write the markdown dry-run report--config FILE— config file (elseTORRENTNGD_CONFIG/ defaults); this locates the TorrentNG-client DB,.torrentblobs, and fast-resume directory--yes— skip the confirmation prompt with--apply
The TorrentNG-client state is never modified. The dry-run report and post-apply summary break torrents into recheck-free / complete-only / metadata-only / torrent-only so you can see how much seeding state survives the move:
| Target | Layout written | Fidelity |
|---|---|---|
libtorrent |
<hash>.torrent + <hash>.fastresume (qBittorrent BT_backup / Deluge state dir) |
Recheck-free, partials included |
transmission |
torrents/ + resume/ |
Recheck-free, partials included |
rtorrent |
<hash>.torrent + <hash>.rtorrent session resume file |
Recheck-free for complete torrents; partials metadata-only (rTorrent's format has no decodable partial bitfield) |
utorrent |
aggregate resume.dat |
Recheck-free at whole-piece granularity |
biglybt |
aggregate downloads.config |
Recheck-free at whole-piece granularity |
generic |
<hash>.torrent files + manifest.json |
Always correct; destination rechecks |
Example — leave for qBittorrent with seeding state intact:
torrentngd export --format libtorrent --to /tmp/leaving --report /tmp/exit.md
# review the fidelity summary, then:
torrentngd export --format libtorrent --to /tmp/leaving --applyThen point qBittorrent/Deluge at the exported directory (qBittorrent: copy
into BT_backup/). generic is the universal safety valve: it always
produces usable .torrent files plus a manifest of save paths, categories,
tags, counters, and completed-piece counts.
Torrents imported via torrentngd migrate --apply persist their .torrent
blob into the session directory, so a migrate-in then export-out round trip
works without first starting torrentngd. A torrent with no TorrentNG-client fast-resume
state exports as torrent-only (the destination rechecks) — that is honest,
not a failure.
If migration causes problems:
- Stop the new container.
- Restore the TorrentNG-client DB/session backup created before import.
- Keep the original source client session untouched and restart it if needed.
- Re-run the dry-run report after correcting path/category/tag remaps.
No migration step modifies the session directory in-place. The import is read-only from the source. See BACKUP_RESTORE.md for TorrentNG-client DB backup and restore commands.