The debrid baseline
What zurg costs and how fast it feels on the debrid path — Real-Debrid, AllDebrid and TorBox. There is no competitor here. The contender is a later zurg, and this file is what it has to beat.
- Build under test:
23869fa4(2026.08.18.0140-nightly-48-g23869fa4) - Measured: 2026-08-19, on
zen(2 cores, 7.6 GB, Ubuntu 22.04) - Rig:
scripts/dbench/, deployed at~/dbenchon zen
How to read it
Three providers are not three competitors either. Their libraries differ by two orders of magnitude — 3416 torrents on Real-Debrid, 479 on TorBox, 14 on AllDebrid — so an absolute cold-build time says more about the account than about zurg. Every size-dependent number is therefore given per torrent as well, and the comparison that carries weight is always this build against the next one on the same account.
What is genuinely comparable across providers is the shape of the cost: how many API calls a backend needs per torrent, whether it pays for link verification at all, whether its change poll is a page or a token.
Configuration
Each instance runs one provider, on its own port, with its own data directory. The deviations from shipped defaults are only those isolation demands:
Everything else is left at the shipped default on purpose, including two that shape the numbers badly and are themselves findings:
log_level: DEBUG— the shipped default (pkg/logutil/factory.go).torrents_rate_limit_per_minute: 75— the shipped default, and the binding constraint on a cold Real-Debrid build.
Corpus
Twelve releases, the same infohashes on all three accounts, so a per-provider
difference is the provider and not the file. Sizes run from a 1.5 GB episode to a
334 GB remux pack; shapes cover single-file movies, season packs, and a 349-file
series pack. Entry 0 — The Shawshank Redemption (1994) DS4K, 7.31 GB, one file —
is the streaming subject for the latency and throughput phases.
Assembling it is the awkward part and is worth recording. TorBox's cache overlaps the
Real-Debrid library by about 2% (3 hits in 136 probes), so the corpus has to be driven
from TorBox's side rather than Real-Debrid's. AllDebrid held nothing and took the
magnets instantly, 14 of 16. Real-Debrid needed 20-45 s between adds against the 451
throttle, and refused two: one is the documented deterministic name block
(Supergirl…WEB-DL…, refused on a clean first attempt after a three-minute quiet
period), the other stayed throttled.
Measuring API cost
zurg keeps no outbound request counter, and log_requests deliberately skips two
Real-Debrid endpoints — torrents/activeCount and torrents?limit=1&… — which
between them are the entire change-detection poll. Counting from the log would
therefore report Real-Debrid's idle cost as zero.
So the count is taken outside the process. mitmproxy terminates TLS, an addon writes
one JSONL line per request, and the instance is started with HTTPS_PROXY and
SSL_CERT_FILE pointing at the intercepting CA. Go's x509 loader honours
SSL_CERT_FILE, so the trust is scoped to that one process and nothing on the host is
modified. Requests are classified api, nettest, content or other.
The proxy sits in the request path, so it is used for the API-cost phases only; the timing phases run direct. On TorBox — where the whole build is four calls — a proxied run and a direct run came out at 3.0 s and 4.0 s, so at this scale the proxy is inside the noise.
Results
All numbers from build 23869fa4 on zen, 2026-08-19. Latency and throughput come
from one interleaved run over the same 7.31 GB file — same infohash on all three
accounts, and ffprobe reports the identical 8552.574 s duration through each, so
the content really is byte-identical.
Cold library build
Empty caches to a compiled library. This is the one number that is not comparable across providers — the accounts differ by two orders of magnitude — so read the per-torrent columns.
Real-Debrid's 7,442 calls break down as 3,414 torrents/info — one per torrent,
unavoidable without ListIncludesFiles — and 3,992 unrestrict/link, which is
proactive link verification. It is the only backend with StoredLinksCanRot: true,
so it is the only one that pays this. TorBox answers the whole scan with a single
mylist, which is what ListIncludesFiles buys.
The Real-Debrid build was run twice, and the two agree closely enough that a future build's improvement will not be noise:
What the failures are
The second run kept its flow log, so the 699 have a breakdown:
Two separate problems, neither of them "the provider was flaky":
-
589 rate-limit rejections, all on
unrestrict/link.api_rate_limit_per_minutedefaults to 250 and does not keep unrestrict inside whatever Real-Debrid actually allows, so roughly 8% of the cold build is refusals and retries. -
22 of 23
downloads/deletecalls returned 404. The cleanup that is meant to balance 3,992 unrestricts is not merely rare, it fails when it runs — it is deleting ids that do not exist. Every unrestrict mints a persistent entry in the account's downloads list, and after these scans the test account held 12,469.
One number moved between runs and is not yet explained: HEAD probes against
*.download.real-debrid.com were 88 in the first build and 3,426 in the second
(about one per torrent). It correlates with the downloads list having grown, and the
second run also fetched /downloads eight times against the first run's one, but
that is a correlation and not a cause. Worth pinning down before it is optimised.
Warm restart
The cold cost is genuinely one-time.
1,774 s becomes 4.0 s, and 7,442 calls become 17.
Idle steady state
Ten minutes of a loaded library doing nothing. The ranking is set by what each
backend can be asked cheaply, and it is the clearest illustration of why
LibraryFingerprinter exists.
Real-Debrid spends 32 of its 114 calls re-listing /downloads — the same list the
cold build grew to 12,469 entries.
This table is also the reason the count is taken through a proxy rather than from
log_requests: 80 of Real-Debrid's 114 idle calls are the two endpoints
ShouldSkipLogging suppresses, so the log would have reported its idle cost as
roughly zero.
Listing
Not a bottleneck anywhere, at any library size.
Press play
One 7.31 GB file, each read on a freshly started instance.
The warm re-read is the same as the cold read. On the Usenet path a repeat read
comes back in 0.05-0.17 s from the 512 MB LRU; here it costs a full fetch, because
the debrid path proxies the CDN and caches nothing. With rclone_enabled: false
this is zurg unaided — in a normal install rclone's VFS cache absorbs it — but as a
property of zurg itself, there is no warm path at all.
Throughput
256 MB sequential reads, three reps, order rotating between reps.
TorBox is both the fastest and much the steadiest; the other two swing by 40% between reps. Real-Debrid's median TTFB under load is four times AllDebrid's.
Where cold open actually goes
apiref.py measures the provider's own half — the call that turns a stored file into
a fetchable URL, and the first byte off that URL — with zurg out of the path. It was
run twice; the first run predates the AllDebrid fix, so that row has one sample.
The provider figures and the zurg figures come from different moments, so the subtraction is indicative rather than exact. The pattern holds across both runs: AllDebrid and TorBox add essentially nothing over what the provider itself costs, Real-Debrid adds 0.75-0.89 s. That gap is the single clearest thing to aim at.
All three providers in one instance
The merged instance carries every account at once, and the corpus is deliberately on all three, so it exercises the path where one release is held by several accounts.
Adding TorBox and AllDebrid to a Real-Debrid instance is close to free: 465 more torrents for 49 more API calls and 5 MB of RSS, and no measurable time. The merge is real — 3,809 torrents across the three accounts separately become 3,781 here, the corpus collapsing from three copies to one.
Add and delete detection
How long from a change at the provider to the mount agreeing.
Both are inside one check_for_changes_every_secs tick plus a refresh, so neither is
waiting on library_resync_every_mins. TorBox was not tested: the probe release is
permanently on that account and deleting it is not reversible without a re-add that
might not be cached.
Provider outage, CDN up
ca/outage.py makes every provider API call return 503 while content requests pass
through, then zurg is started into it.
Starting zurg while its provider is down produces an empty mount. That is the
scenario that matters operationally — a host reboots during a Real-Debrid 503 spell —
and it is the exact condition that destroyed tens of thousands of Plex items on fun in August,
where a momentarily empty mount during a scan reads as "every file was deleted". The
guard for that lives in Plex (autoEmptyTrash 0), not in zurg.
Real-Debrid and AllDebrid additionally make the reader wait out its own timeout rather than failing fast; TorBox answers in 35 ms.
A transient 503 leaves releases as empty folders
The clearest defect of the run, and it was found by accident.
Of the 3,316 torrents in a Real-Debrid cold build, 85 responses were 503 and
84 torrents ended up in broken_torrent. A release in that state is still listed
in the mount, but its directory is empty — verified by PROPFIND against two of
them, both holding 10 and 3 selected files in their stored record and serving none.
It does not clear by itself. The bench runs with enable_repair: false, and those
releases were still broken more than an hour and several restarts later.
Turning repair on — the shipped default — does work through them, but slowly. With a pass triggered by hand:
Twelve releases in a quarter of an hour, and the rate falls off after the first minutes. On that trajectory a single cold build's worth of breakage takes upwards of an hour to clear, and every unreached release is an empty folder for the whole of it.
So the severity depends on a setting:
-
With repair on (default), a 503 during a scan costs a release its files until a repair pass reaches it — order of an hour for a full library.
-
With repair off, the release is an empty folder indefinitely.
TorBox has the sharper version of this. It reports StoredLinksCanRot: false, so
refresh never re-verifies its links — there is no path that clears a broken state
short of repair. The one TorBox release marked broken during the outage test was
still broken 68 minutes and several restarts later, while its provider reported it
cached, download_present, one file, 7.31 GB.
Read integrity
One AllDebrid read in the first throughput run returned 263,798,425 bytes for a 268,435,456-byte range, with a 206 and no error. It did not reproduce: eight repeats each on Real-Debrid and AllDebrid returned the full range every time. Recorded as a one-off rather than a defect, and worth watching for.
Under Plex's load
Everything above is one reader at a time, which is not how a media server behaves. Plex scans — thousands of files, a little of each — and Plex streams, and the two overlap. These phases measure the mount under both.
Read the aggregate figures as a property of zen, not of zurg. Every target plateaus at 73-78 MB/s whatever the provider and whatever the mode, which is the host's link. What generalises is the completion rates and the per-provider failure shapes; the ceiling is local.
Multiple readers, different files, one provider
96 MB per stream, distinct files.
Aggregate MB/s. The pipe is full at two streams; past that, more readers divide the same bandwidth — per-stream median falls from ~45 MB/s at one to ~8 MB/s at twelve.
Completion is the interesting column:
AllDebrid stops completing reads from four concurrent distinct files. Bytes still flow — its aggregate stays at the host ceiling — but individual streams do not finish, and the per-stream median drops to zero at eight and twelve with TTFB medians above 3 s. Everything else holds until twelve, where all four lose one or two.
Retracted 2026-08-23. This is not a provider concurrency limit. Retested direct to the APIs at rate-capped playback bitrates rather than flat-out through zurg, AllDebrid resolved 4/8/12/16/24 concurrent distinct links at 100% and sustained 24/24 live streams. The failures above were three null-routed AllDebrid CDN hosts —
l4m5n6.debrid.it,o7p8q9.debrid.it,c1uw74.debrid.it, all resolving to0.0.0.0and carrying 15.6% of resolved links. The first is the same host recorded as a one-off dead link further down this document; it is not a one-off.A dead host fails only when its file is in the batch, so the failure count rises with N and takes exactly the shape of a concurrency ceiling. Any ladder that draws more distinct files as it climbs will reproduce this false result.
The whole completion column here is method-limited for the same reason: these runs pulled flat-out over a saturated 73–78 MB/s link, so what it measures is bandwidth share, not a provider limit. The real per-host ceilings, all undocumented, are Real-Debrid 32 (dropped at the TCP layer), AllDebrid 32 (HTTP 429), TorBox 20 per
nexus-NNNhost (HTTP 429, per host rather than per IP), and no ceiling found for Premiumize at 64.One caveat against over-reading the retraction: a null-routed host fails in ~0.1 s, whereas the TTFB medians above run past 3 s. That is a slow failure with a different signature, so bandwidth starvation was probably present too. Both effects point away from a provider cap.
The fix this implies: treat a connection-level failure on an AllDebrid CDN host as retryable by re-unlocking the link — a fresh
/link/unlockreturns a different host — not by reducing concurrency. A DNS lookup rejecting0.0.0.0catches every case seen here and costs nothing.
Multiple readers, one file
Same levels, one file, different offsets — the read-ahead-plus-seek shape.
AllDebrid is fine here — 4/4 and 8/8 — while it was 2/4 and 3/8 on distinct files. That inversion is the diagnosis: its limit is not bandwidth and not a per-torrent cap, it is concurrent link resolution. Distinct files mean distinct unlocks; one file resolves once and then just streams.
Retracted 2026-08-23 — the inversion has a simpler explanation. Distinct files draw distinct CDN hosts, and three of AllDebrid's were null-routed; a single file resolves to one host and either works or does not. Link resolution itself was measured clean at 24 concurrent. See the note above.
Multiple readers across different providers
This is the mixed-provider case, and it needs a corpus the matched one cannot
provide. Since the merged instance serves a multi-account release from Sources[0],
every read of the matched corpus lands on Real-Debrid. So the pool here is
provider-exclusive: 8 files only Real-Debrid holds, 7 only AllDebrid, 8 only TorBox.
Per-provider columns are per-stream medians in MB/s.
Reads on different accounts do genuinely run in parallel — but they arrive at the same 76 MB/s host ceiling that one provider reaches on its own. On this host, mixing backends buys no bandwidth. Whether it buys any elsewhere depends on having a link fatter than one provider will fill, which zen does not. What it buys instead is availability — and only partly, as the failover section below shows.
The consistent one failure is a single AllDebrid file whose CDN hostname,
l4m5n6.debrid.it, resolves to 0.0.0.0. That is AllDebrid handing out a dead
link, not zurg — but zurg spends about 4 s on three retries before returning 500, and
because the file is on no other account there is nothing to fall back to.
Failover: only from one direction
Since the merged instance cannot spread load, the thing it is supposed to buy is
survival of one account going bad. ca/cdnfail.py refuses one account's CDN while
leaving every API and the other accounts alone, so the library still loads and only
the bytes are affected. The releases read are the matched corpus — all three accounts
hold every one of them.
Failover only happens when the failing account is Real-Debrid, and the reason is a
capability the other backends do not set. Refresh proactively verifies links only when
StoredLinksCanRot is true, which is Real-Debrid alone:
Link verification failed during refresh for /A.Business.Proposal…: (code: 503)
That is what demotes the source, after which reads move to another account cleanly. AllDebrid and TorBox are never probed, so a dead CDN on either is never noticed, the source is never demoted, and every read of that release returns 500 — for as long as the condition lasts.
Two further things this shows:
-
The read path has no inline fallback at all. A 503 from the chosen account's CDN is turned straight into a 500 for the client in 0.1 s; the other accounts holding the same file are not tried. Recovery is entirely the background refresh's job, so even in the Real-Debrid case there is a window where reads fail.
-
It is the same missing mechanism behind the TorBox release that stayed
broken_torrentfor 68 minutes. Nothing re-verifies a TorBox or AllDebrid link, so nothing clears a bad state or notices a recovered one.
A dead link from the provider
One AllDebrid file in the exclusive pool failed every single time it was touched — in each scan run and each mixed-reader run. Its CDN hostname resolves to a null route:
$ getent hosts l4m5n6.debrid.it
:: l4m5n6.debrid.it
0.0.0.0
AllDebrid handed out a link pointing at 0.0.0.0. That part is the provider's, not
zurg's. zurg's part is what it does about it: three attempts over roughly 4 s, then a
500. The file is on no other account, so there was nothing to fall back to — but the
failover result above says it would not have fallen back anyway.
2026-08-23: not one host, and not one file. Three AllDebrid CDN hostnames were null-routed simultaneously —
l4m5n6.debrid.it,o7p8q9.debrid.it,c1uw74.debrid.it— accounting for 5 of 32 links in one sample and 4 of 32 in another, across unrelated content. AllDebrid hands the same dead hosts out for different files, and was still doing so days after this run. This is a persistent hole in its fleet, and it is what produced the retracted concurrency finding above.Three attempts and a 500 is the wrong response. Re-unlock instead —
/link/unlockreturns a different host — and pre-flight with a DNS lookup that rejects0.0.0.0, which catches every case observed at no cost.
Scanning
ffprobe over the whole 23-file mixed pool, which is the shape of a Plex analysis
pass.
Scanning parallelises well — 3.3x from one thread to eight, with per-file latency only rising 60%. For the 3,316-release Real-Debrid library that is roughly 76 minutes at one thread and 23 minutes at eight, before Plex's own work.
Streaming while scanning
The case worth fearing, since a scan against a momentarily unhappy mount is what cost
tens of thousands of Plex items on fun.
A scan does not starve a stream. The viewer keeps essentially all of its throughput, and its time-to-first-byte actually improves (1.18 s to 0.15 s) because the link is already warm by then. This is the one place the debrid path behaves better than feared.
Defects this run surfaced
Ordered by what they cost.
-
A transient 503 leaves a release as an empty folder. 85 x 503 during one cold build put 84 releases into
broken_torrent, and a broken release is listed with no files in it. With repair off it never clears; with repair on it clears only when a pass reaches it. On TorBox there is no re-verification path at all (StoredLinksCanRot: false), and the one release broken there was still broken 68 minutes later while the provider reported it cached and present. -
Download-entry cleanup is broken, and the cost compounds. 3,992
unrestrict/linkagainst 23downloads/delete, and 22 of those 23 returned 404 — the cleanup deletes ids that do not exist. The account went from 12,469 entries to 18,768 over two further cold builds, and zurg pages the whole list at startup 5,000 at a time, so the leak turns into a startup cost that grows with it. -
589 rate-limit rejections in one cold build. All on
unrestrict/link, about 8% of the pass. The defaultapi_rate_limit_per_minute: 250does not hold unrestrict inside Real-Debrid's real limit. These and the 503s are the same event that produces defect 1. -
Starting into a provider outage yields an empty mount, and a read against Real-Debrid or AllDebrid then hangs until the client's own timeout rather than failing fast. TorBox answers 400 in 35 ms.
-
AllDebrid stops completing concurrent reads of different files.Retracted 2026-08-23 — see the note in "Multiple readers, different files" above. The cause was three null-routed AllDebrid CDN hosts carrying 15.6% of links, not a concurrency limit; retested directly, AD resolves 24 concurrent distinct links at 100% and sustains 24/24 streams. The original text is kept below for the record.2 of 4, 3 of 8, 4 of 12. The same levels against a single file are fine (4/4, 8/8), so the limit is concurrent link resolution, not bandwidth or a per-torrent cap. Four files playing at once is an ordinary evening for a household.
-
A multi-account release always serves from the same account.
File.PrimarySourcereturnsSources[0], so the choice is whichever account was seen first — verified 7 of 7 corpus releases served by Real-Debrid while AllDebrid and TorBox held identical copies. It never picks the faster or less loaded account. -
Failover works in one direction only, and never inline. With Real-Debrid's CDN refusing, reads moved to AllDebrid; with AllDebrid's refusing, 0 of 3 reads were served and each returned 500 in 0.1 s without trying the two accounts that held the same file. Only Real-Debrid sets
StoredLinksCanRot, so only its links are re-verified during refresh, and only its failures demote a source. Nothing in the read path tries a second account on an error. -
The startup network test runs for every backend. 88 HEAD requests against Real-Debrid download hosts on a TorBox-only and an AllDebrid-only instance, which have no use for the result.
-
A collection's own PROPFIND href 404s. zurg emits
<d:href>/all/…</d:href>, dropping the/davprefix; fetching that path returns 404. Children are emitted as bare relative hrefs, so only the self entry is wrong. RFC 4918 wants an href a client can resolve, and rclone and Plex only cope because they do not use it. -
user/user/meis called three times on every startup, cold or warm, on both TorBox and AllDebrid.
Not defects, but worth knowing
-
Real-Debrid does not deduplicate by infohash. Adding a magnet already on the account creates a second entry. That is why the 3,416 torrents compile to 3,318 unique releases, and it invalidated the first delete-detection probe, which deleted the duplicate it had just created and concluded nothing was ever detected.
-
torrents_rate_limit_per_minutedefaults to 75, which is what bounds the cold Real-Debrid build. It is a deliberate default, not a bug, but it is the number that sets the 29-minute figure.
Rerunning
cd ~/dbench && ./runall.sh # p1..p6
./p7.py adddel rd,ad && ./p7.py outage rd,ad,tb
./apiref.py rd,ad,tb # provider-side reference latency
./report.py # tables
Never run two instances at once, and only compare numbers from a single interleaved run — provider throughput drifts widely across an evening.