Per-Torrent Rate Limits
Cap download and upload speed for a single torrent, independent of the global limit.
The global limits in Settings → Connection apply to the engine as a whole. Per-torrent limits, set in the inspector’s Overview tab, cap a single torrent’s download or upload speed regardless of how much headroom the global limit has left.
The most common use case: cap upload on a specific seedbox torrent so it doesn’t saturate your link, without having to lower the global upload cap (which would affect every other torrent too).
Where to find it
Inspector → Overview tab → the two rate-limit rows below the totals:
- ↓ Speed limit — KB/s, or empty for unlimited.
- ↑ Speed limit — KB/s, or empty for unlimited.
Values are entered as KB/s, persisted to the torrents table (down_rate_limit, up_rate_limit columns in bytes/sec), and re-applied on startup. Clearing the input (or entering 0) removes the cap.
How it works (implementation note)
anacrolix v1.61 has a per-Client rate limiter (which is what powers the global cap) but no per-torrent limiter API. Mosaic implements per-torrent caps with a background duty-cycle goroutine: every 500 ms it samples the torrent’s byte counters and toggles DisallowDataDownload / AllowDataDownload (and the upload equivalents) to throttle when the previous interval exceeded budget.
Consequences:
- Coarse, not byte-perfect. Actual throughput hovers around the configured cap ± one tick’s worth of in-flight data (~one chunk, ~16 KB by default). For typical caps (tens of KB/s or more), the visible behavior matches the configured value within a few percent. Below ~16 KB/s, the duty cycle gets bursty.
- One goroutine per limited torrent. The goroutine starts the first time a limit is set and exits when both axes drop back to 0. Setting limits on hundreds of torrents simultaneously creates hundreds of goroutines — fine in practice (they’re cheap and mostly sleeping), but worth knowing if you’re scripting bulk operations.
- Pause path is independent. The duty-cycle limiter and
Pause()both useDisallowDataDownload/Uploadunder the hood, but the engine tracks which call last set the state so a manual pause-then-resume doesn’t accidentally drop your limit, and a limit hitting 100% block doesn’t trick the UI into showing the torrent as paused.
Interaction with the global cap
Per-torrent and global caps compose; the effective speed is the minimum of all applicable caps. Examples:
- Global down = 1000 KB/s, torrent down = 200 KB/s → this torrent gets at most 200 KB/s.
- Global down = 100 KB/s, torrent down = 200 KB/s → this torrent gets at most 100 KB/s (the global is tighter).
- Global down = 0 (unlimited), torrent down = 200 KB/s → this torrent gets at most 200 KB/s; other torrents are unlimited.
The scheduled-bandwidth rules from Scheduling only mutate the global limit — your per-torrent caps stay in force across schedule windows.
API
GET /api/torrents/{id}/rate_limits → { "down_kbps": 0, "up_kbps": 200 }
PUT /api/torrents/{id}/rate_limits body: { "down_kbps": 0, "up_kbps": 200 }
0 means unlimited on that axis. Values are in KB/s; the engine stores bytes/sec internally and reapplies on startup. Requires editor-or-owner access on the torrent.
See also
- Sequential download — the other Inspector-tab control
- Scheduling — schedule global caps on a clock
- Seed limits — stop the torrent entirely instead of just slowing it