After moving into a new place with a not-so-hot upload, I found my torrents were eating my bandwidth. 98% of the traffic was from torrents and my house was contending over the last 2% with some pretty wild latency.
The VM is a Windows 11 box called Torrent, running qBittorrent 5.1.0 behind ProtonVPN’s split tunnel, and its whole job is downloading Linux ISOs. A lot of Linux ISOs. Since August it has picked up limits in several places: rate caps and a queue limit inside qBittorrent, Smart Queues on the UDM Pro’s WAN, and a UniFi QoS rule aimed at its MAC address. This post goes through what each one is set to today and whether it’s the one holding the traffic back. A couple of them aren’t.
All the numbers below were read on 23 October 2026, from qBittorrent’s WebUI API and the UniFi controller’s API, unless I say otherwise.
Rate caps inside qBittorrent
The caps went in where the traffic starts. qBittorrent stores its settings in qBittorrent.ini, but it rewrites that file itself, so I don’t edit it. I read and change settings through the WebUI API. On my network the API answers without a login from my apphost VM at 192.168.66.11, because WebUI\AuthSubnetWhitelist covers that address (and the tailnet range). From anywhere else it wants a session cookie.
This is the read I ran from apphost, filtered down to the settings that limit something:
And the part of the output this post is about:
| |
The API reports rates in bytes per second, while the WebUI shows KiB/s. dl_limit of 12,800,000 B/s is the 12,500 KiB/s you see in the WebUI, and on the wire it’s 102.4 Mbps. up_limit of 614,400 B/s is 600 KiB/s, a little under 5 Mbps.
The download cap has been 102.4 Mbps since August. The upload cap started at 1 MB/s and came down to 600 KiB/s on 23 September, for reasons that get their own section below. When I read the transfer counters this afternoon, upload sat at 610,458 B/s against that 614,400 cap, so this one binds all the time. Download was well under its cap, because nothing much was downloading.
The cap counts payload
limit_tcp_overhead = False means qBittorrent applies the limits to torrent payload only. TCP/IP headers, protocol messages and tracker traffic are extra. In my measurements the 102.4 Mbps download cap showed up as about 110 Mbps on the wire. If you size a cap against your link, leave room for that, or turn on “Apply rate limit to transport overhead” in the WebUI’s Speed settings and accept that payload speed drops.
limit_utp_rate = True is the other one to check. uTP is BitTorrent’s UDP-based transport, and if that box is unticked, uTP traffic ignores the caps.
Connections
I also cut the connection limits in August. On 30 September I found the global limit saturated at 297 of 300 and raised it to 800, after which established connections settled around 419. That fixed a real ceiling, but it wasn’t the cause of the stalled torrents I was chasing that night.
One caution if you count connections through the API: summing num_seeds and num_leechs across torrents gave me 110 while the VM showed 377 established TCP connections on the tunnel address. When I need the real number, I count sockets on the host now.
A queue limit of 9 with 233 running
In the past I didn’t have a queue limit and let my seeds go wild. A slower connection, and free storage shrinking at current drive prices, made me turn queueing on.
qBittorrent’s queueing system caps how many torrents download at once. Mine was set to 9. On 22 September there were 233 torrents downloading, with 105 files open on the SMB share at once.
The cause was a pair of settings that sound sensible on their own. dont_count_slow_torrents was true, with slow_torrent_dl_rate_threshold at 50 KiB/s. A torrent moving slower than 50 KiB/s stops counting against the active limit, so qBittorrent starts another one in its place. Every stalled torrent is slower than 50 KiB/s. With a big queue and plenty of dead torrents, the “limit” of 9 became a rule for how many fast torrents to run, and everything else ran too.
Most of the cost landed on storage. Each started torrent keeps its partial data, so 229 of the 686 queued torrents held 2.23 TB between them, and the incomplete folder reached 6.2 TB across 1,175 unfinished torrents. I thought I’d never run into storage issues at home with 120 TB across three different RAIDs, but I feel like the universe fills the storage once you have it built out.
The fix was two settings:
With the slow-torrent exception off, 16 means 16. I raised the number from 9 because about 74 torrents complete on a typical day and 76 get added (there are more distros than you’d think), and a hard 9 with a few slow but alive torrents in it couldn’t keep up. A stuck torrent now holds a slot it isn’t using, so something has to clear dead ones out. A cleanup tool removes torrents that have stalled for too long, and its rules needed tightening the same day. That’s more of a storage and automation story than a networking one, so I’ll leave it there.
Shaping the whole WAN to 260 Mbps
Smart Queues sits on the gateway and covers every client. My Cox plan is provisioned at about 886 Mbps down and 34 Mbps up. Smart Queues on the UDM Pro shapes the WAN to 260 Mbps down and 25 Mbps up.
The controller keeps those values on the WAN network object. I read it through the classic Network API from my Raspberry Pi, using a local admin with the Viewer role (the same read-only account my WAN monitor uses). UniFi OS wants a login before it answers anything, so the script posts credentials to /api/auth/login, keeps the session cookie, and then calls the Network application through /proxy/network/:
| |
cfg comes from a root-only config file on the Pi, so the credentials never sit in the script, and g() is a small wrapper around op.open() that parses the JSON. Certificate checks are off because the script connects to the gateway by its IP address. The cookie jar carries the session from the login to every call after it.
The output, trimmed to the Smart Queues fields:
Rates are in kbps, so 260000 is 260 Mbps. In the UI it’s the WAN’s settings under Internet, with Smart Queues and its download and upload fields.
Ubiquiti’s own guidance is that Smart Queues is for connections under 300 Mbps, and they “do not recommend enabling Smart Queues for ISP connections that provide 300 Mbps or higher speeds.” My connection is well above that. I’ve put a shaper at 260 Mbps on it anyway, which means I’m choosing to throw away about 70% of my download speed.
I chose latency over throughput on this connection. Several people in the house play some MMOs and FPS where it’s super serious business when latency drops. A shaper only controls latency when its rate is below the real bottleneck, because that’s what moves the queue from the modem and the ISP’s equipment (where I can’t see or manage it) into the gateway, where Smart Queues controls it. Setting the rate far below the plan gives that a lot of margin.
Looking at historical speed tests, 260 seemed to be the magic number.
Upload is where the shaping bites. 25 Mbps is about 74% of the provisioned 34, and upload is where a torrent client and a Plex server compete. The torrent VM’s 600 KiB/s cap takes about a fifth of the shaped upload, leaving the rest for everything else, Plex’s remote streams included.
Priority for two clients
Two QoS rules mark traffic for priority. They come from a different endpoint, the v2 API’s qos-rules. This is the relevant part of the JSON, with the MAC addresses removed:
betarpc priority at index 10003 is the same rule for my main PC. Ubiquiti describes the Prioritize objective as moving matched traffic “into a higher-priority queue to improve performance.” As I understand it, a priority queue only has an effect where traffic is waiting in a queue the gateway controls. With a 260/25 shaper in front of the WAN, the gateway is where traffic waits. Without Smart Queues, the queue forms upstream and the gateway has nothing to reorder.
The priority is for me: betarpc is my PC, and I’m the most active user on my Plex, rest of the network be damned.
A throttle rule above every other limit
The UniFi throttle is the limit that does nothing today. It’s a QoS rule with the Limit objective, aimed at the torrent VM’s MAC address:
| |
400 Mbps down and 8 Mbps up. The download half can’t bind under a WAN shaped to 260 Mbps, and qBittorrent’s own 102.4 Mbps cap sits below both. For download, this rule is the third-tightest limit on a path where only the tightest one counts.
The upload half was closer. qBittorrent’s old 1 MB/s cap is about 8.4 Mbps of payload, more with overhead, so before 23 September the UniFi rule’s 8 Mbps was probably the lower of the two. Since the cut to 600 KiB/s, qBittorrent holds upload under 5 Mbps on its own, and the UniFi rule is above that as well.
The split tunnel doesn’t change any of this. qBittorrent’s packets leave the VM inside the VPN tunnel to ProtonVPN, but they still come from the VM’s MAC address, so the gateway’s MAC-based rule and the WAN shaper both see them as one client.
I’ve left the rule enabled. It costs nothing, and if I ever raise or remove the caps in qBittorrent, it’s a ceiling that lives outside the VM.
Reading a 26 ms average
The upload cut on 23 September came out of looking at latency.
UniFi’s dashboard showed 26 ms average latency, which sounds fine. That average hides the shape. From the house, the LAN was 0.1 ms to the gateway and 0.4 ms host to host. To 1.1.1.1 it looked like this, measured while qBittorrent pulled 98 Mbps down and 8 Mbps up:
| Percentile | Latency to 1.1.1.1 |
|---|---|
| p50 | 16 ms |
| p90 | 44 to 105 ms |
| p99 | 178 ms |
| max | 214 ms |
That’s two ping runs from ms01a a minute apart, 25 pings and then 40, laid side by side: the p90 is the two runs’ figures, the max came from the shorter run and the p99 from the longer. 15% of pings came back over 50 ms. A median of 16 ms with a tail like that is bufferbloat: most packets get through quickly, and the ones that land behind a full buffer wait a long time. The 26 ms dashboard figure is a blend of the two.
My Uptime Kuma instance has a “WAN latency” monitor fed by the controller’s own latency figure, and its longer view agreed. The minimum was a steady 9 to 12 ms every hour, while hourly averages swung between 25 and 75 ms, with spikes past 1.6 s and the worst of it in the evenings.
I could retune Smart Queues, or I could take load off the uplink. I’d already been through the UniFi side and found that changing it made no difference, so I cut qBittorrent’s upload cap from 1 MB/s to 600 KiB/s instead. Afterward the VM measured 4.2 Mbps up.
Then I ran another 60 pings to see if it had helped:
| Before | After | |
|---|---|---|
| p50 | 16.0 ms | 14.3 ms |
| p90 | 105 ms | 133 ms |
| max | 180 ms | 795 ms |
| Pings over 50 ms | 15% | 8% |
The median improved, fewer pings crossed 50 ms, and the tail got worse. That sample says nothing in either direction. On my connection the variance from one hour to the next is bigger than the effect I was trying to measure, so 60 pings taken a few minutes apart compare two moments, not two configurations.
The comparison that would work is hourly averages at the same time of day, over days, before and after. I can’t make it cleanly. Kuma’s hourly table on my Pi keeps a rolling 30 days, and today the oldest row is 23 September at 21:00 UTC, which is after the change. So there’s no “before” left to compare against. I didn’t even set up the WAN latency monitor in Kuma until 10 September.
What the 30 days after the change do show is the shape. Averaged by hour of day, Pacific time:
| Hours (local) | Average latency | Lowest hourly minimum |
|---|---|---|
| 00:00 to 10:00 | 23 to 26 ms | 6 to 8 ms |
| 11:00 to 17:00 | 29 to 41 ms | 8 ms |
| 18:00 to 20:00 | 33 to 34 ms | 6 to 8 ms |
| 21:00 to 23:00 | 27 to 29 ms | 7 to 9 ms |
The worst hour is 17:00 at 41 ms on average, and the biggest single spikes, over 3 seconds, all sit between 13:00 and 17:00. Daily averages since 9 October run from 20 to 34 ms. The floor of 6 to 9 ms holds in every hour (apart from one zero at noon that looks like a bad sample), so the path itself is fine and the averages come from load. Whether that load is mine or the neighborhood’s on Cox’s side, this data can’t tell me.
Where each limit sits now
| Limit | Where | Set to | Binding today |
|---|---|---|---|
| Download cap | qBittorrent | 102.4 Mbps payload | Yes, when downloading |
| Upload cap | qBittorrent | 600 KiB/s, about 4.9 Mbps | Yes, all the time |
| Active downloads | qBittorrent | 16, slow torrents counted | Yes |
| Global connections | qBittorrent | 800 | No, around 419 in use |
| Smart Queues | UDM Pro WAN | 260 / 25 Mbps | Shapes the whole house |
| torrent throttle | UniFi QoS rule | 400 / 8 Mbps | No, in either direction |
| Priority | UniFi QoS rules | Plex VM, betarpc | When the shaper is queueing |
The limits that do the work on the torrent VM are the ones inside qBittorrent. Smart Queues does a different job: it keeps the house responsive whatever the VM is doing, and it gives the two priority rules a queue to work with. The UniFi throttle is a backstop that hasn’t had anything to stop since September.
I sized every limit on that list for the link as it ran when I set it.
Sources
- UniFi Gateway - Smart Queues, Ubiquiti Help Center
- UniFi QoS and Traffic Shaping, Ubiquiti Help Center
