Ever run a backup job at 2 AM and come back to find three other VMs on the same host crawling? Or had one noisy container hammer your ZFS pool so hard that everything else on it feels like it's running off a floppy disk? That's usually not a Proxmox problem or a hardware problem. It's a lack of disk I/O limits.
By default, every VM on a Proxmox VE host competes for the same disk queue with no referee. Whoever asks first, or asks hardest, gets served first. On a single-VM homelab box that's fine. The moment you run more than two or three VMs off the same spinning disk, or the same shared storage array, one greedy VM can starve the rest without you ever touching a CPU or RAM setting.
This guide walks through setting actual bandwidth and IOPS limits on a per-disk basis, both from the GUI and from the command line, so you can stop one VM from eating everyone else's storage performance.
What You Will Learn
- What disk I/O throttling actually controls, and what it doesn't
- The difference between a sustained rate limit and a burst pool
- How to set read/write speed and IOPS limits from the Proxmox web interface
- How to set the same limits from the
qmcommand line - Realistic starting values depending on your storage type
- Common mistakes that make throttling seem broken when it isn't
What Is This Feature?
Disk I/O throttling in Proxmox VE is a per-virtual-disk setting that caps how fast a VM (or LXC container) can read from or write to its virtual hard disk. You can cap it two ways: by raw throughput, in megabytes per second, or by the number of I/O operations per second, known as IOPS.
These aren't the same thing, and mixing them up is where most beginners go wrong. A single 1 MB sequential write counts as one I/O operation. A thousand scattered 4 KB writes also happen to add up to roughly 4 MB, but that's a thousand I/O operations, not one. A database doing lots of tiny random writes will hit an IOPS ceiling long before it hits a bandwidth ceiling. A VM copying a large ISO file will do the opposite — it'll hit the MB/s cap while barely registering on IOPS.
Under the hood this is enforced by QEMU's built-in I/O throttling, the same mechanism KVM has had for years. Proxmox just exposes it through the GUI and through qm.conf so you don't have to hand-edit QEMU arguments.
Why Would You Use It?
A few real situations where this actually matters:
- Shared spinning storage. A single 7200 RPM HDD tops out somewhere around 150-200 IOPS. One VM doing heavy random I/O can eat that entire budget and leave nothing for anyone else.
- Backup windows. A
vzdumpjob reads the entire disk of a VM, and on busy nights that read load bleeds into every other guest sharing the storage. Capping the backup source disk's read speed keeps the rest of the host responsive. - Noisy-neighbor containers. A misbehaving app inside an LXC container doing constant log writes can degrade a whole ZFS pool for everyone else on it.
- Fair-use hosting. If you're running Proxmox for other people, even just a couple of friends' VMs, throttling keeps one person's workload from becoming everyone else's problem.
Honestly, if you're running a single VM on dedicated NVMe storage, you probably don't need any of this. Throttling earns its keep the moment storage is shared.
Prerequisites
- A working Proxmox VE host — this guide was written against Proxmox VE 9.x but the same options have worked unchanged since at least 7.x
- At least one existing VM with a virtual disk attached (works for
scsi,sata,virtio, andidedisks) - Root or an account with VM.Config.Disk permission on the guest
- SSH or console access to the host, only needed if you plan to use the CLI method
You don't need any particular storage backend for this. It works the same on local LVM-thin, ZFS, directory storage, or Ceph — the limit is applied at the QEMU device level, not the storage layer.
Step-by-Step Tutorial
Step 1: Decide which numbers you actually need
Before you touch anything, figure out roughly what your storage can deliver. A cheap consumer SATA SSD might do 300-500 MB/s and 40,000-90,000 IOPS on paper, but a lot less once several VMs share it. A single spinning disk is closer to 150-200 MB/s sequential and maybe 100-200 IOPS random. There's no universal "correct" number — you're deciding how much of the pie one VM is allowed to eat, not what the hardware is capable of in isolation.
A reasonable starting point for a shared HDD pool: cap each non-critical VM at somewhere around 30-50 MB/s and 100-150 IOPS, then adjust based on what actually feels usable.
Step 2: Open the disk settings in the GUI
Click your VM in the left-hand tree, go to the Hardware tab, and double-click the disk you want to limit (usually listed as Hard Disk (scsi0) or similar). This opens the disk's edit dialog.
Step 3: Expand Advanced options
Tick the Advanced checkbox in the bottom-left corner of that dialog. This reveals a row of fields you don't normally see: Async IO, IO thread, and — the ones you actually want — Limit I/O MB/s and Limit I/O IOPS.
Step 4: Set your read and write limits
Each of the two limit fields expands into separate read and write boxes when you click into them. You can set an overall combined limit, or split read and write independently — for example, allowing faster reads than writes, which is common since most workloads read more than they write.
Leave a field at 0 (or blank) to leave that direction unthrottled. You don't have to set all four numbers — plenty of people only cap writes and leave reads alone.
Step 5: Apply and verify
Click OK. The change applies live — no reboot, no VM restart needed. To confirm it stuck, check the VM's config from the shell:
qm config 101
You should see the limits appended directly onto the disk line, something like:
scsi0: local-lvm:vm-101-disk-0,size=32G,mbps_rd=50,mbps_wr=30,iops_rd=500,iops_wr=300
Step 6: Set the same limits from the CLI
If you'd rather script this or apply it across several VMs at once, you can do it with qm set. The catch is that qm set rewrites the entire disk line, so you need to include the disk's existing options, not just the new throttle values, or you'll wipe out settings like size or cache.
Pull the current line first:
qm config 101 | grep scsi0
Then reapply it with your throttle values tacked on:
qm set 101 --scsi0 local-lvm:vm-101-disk-0,size=32G,mbps_rd=50,mbps_wr=30,iops_rd=500,iops_wr=300
To remove a limit later, just run the same command again without that parameter — Proxmox doesn't keep old values around once they're dropped from the line.
Step 7: Set a burst allowance (optional)
If a flat cap feels too aggressive for short bursts — say, a VM boot process that needs a quick spike of reads — you can pair a sustained limit with a higher burst pool using the _max variants:
qm set 101 --scsi0 local-lvm:vm-101-disk-0,size=32G,mbps_rd=50,mbps_rd_max=150,mbps_rd_max_length=10
That allows short bursts up to 150 MB/s for up to 10 seconds before dropping back down to the sustained 50 MB/s limit.
Commands Explained
| Command / Parameter | What it does |
|---|---|
qm config <vmid> | Prints the VM's full configuration, including every disk line, so you can see current settings before changing them. |
qm set <vmid> --scsi0 ... | Rewrites the scsi0 disk entry with the options you provide. Any option you leave out is removed, so always include the full existing line. |
mbps_rd / mbps_wr | Sustained maximum read/write speed for the disk, in megabytes per second. |
iops_rd / iops_wr | Sustained maximum read/write operations per second, independent of how much data each operation moves. |
mbps_rd_max / iops_rd_max (and _wr_max) | The ceiling a short burst is allowed to reach before it's throttled back down to the sustained limit. |
mbps_rd_max_length / iops_rd_max_length | How many seconds a burst is allowed to run at the higher _max rate before it's forced back to the sustained rate. |
Common Errors
"parameter verification failed... value does not match the regex pattern" — this almost always means you typed a limit as a whole number with a stray unit attached, like 50MB instead of 50. The value is always a plain number; the unit is implied by the parameter name.
Limits appear to do nothing. Nine times out of ten this is caching, not a broken limit. If the disk is set to Write back cache mode, small writes get absorbed into host RAM first and flushed later, so short bursts of write activity won't visibly hit your write limit — only sustained writes will. Switch to No cache temporarily if you want to confirm the limit is actually being enforced.
The whole disk line got reset. This happens when you run qm set with only the new throttle parameters and forget to include size, cache, or other existing options. Proxmox won't merge your change into the existing line — it replaces it outright.
Troubleshooting
If you set a limit and performance inside the guest doesn't seem to have changed at all, check whether you're testing from cache. Run a benchmark with a tool like fio using --direct=1 so reads and writes bypass the guest's own page cache — otherwise you're measuring RAM speed, not disk speed, and no storage throttle in the world will show up in that number.
If the VM feels slower than the number you set implies, remember that IOPS and MB/s limits apply independently — whichever one you hit first is the one that governs. Setting mbps_rd=100 with iops_rd=50 means a workload doing small 4 KB reads will hit the 50 IOPS ceiling long before it gets anywhere near 100 MB/s of throughput.
For LXC containers, the same throttling exists but lives on the mount point line instead of a scsi/virtio disk line — check pct config <ctid> and look at the rootfs or mp0 entry, the parameter names are identical.
Best Practices
- Throttle write speed more aggressively than read speed on shared storage — writes tend to cause more contention, and most workloads read far more than they write anyway.
- Set limits per-VM based on how many VMs actually share that storage, not on what the disk can theoretically deliver alone.
- Use a burst pool (the
_maxoptions) for anything with a boot process or startup spike, rather than setting one flat number that punishes normal startup behavior. - Document why a limit is set — a plain
mbps_wr=20in a config file six months from now with no note attached is a mystery you'll have to re-solve. - Re-test limits after moving a VM's disk to different storage. A value tuned for a spinning-disk pool is usually far too conservative once that disk lives on NVMe.
Frequently Asked Questions
Does this affect the host's own disk performance too, or just the VM?
Only the VM (or container) the limit is attached to. The host itself, and every other guest, is unaffected — that's the entire point of setting it per-disk rather than globally.
Can I apply a limit to every VM on the host at once?
Not as a single host-wide setting through the GUI. You set it per virtual disk. If you want the same limits across many VMs, script it with a loop over qm set calls, or apply it as part of your VM template so every clone inherits it.
Will this slow down backups?
If you throttle the disk a vzdump job is reading from, yes — the backup will take longer to complete, because it's reading through the same throttled path as everything else. That's often a reasonable trade: a slower backup instead of a host that grinds to a halt while it runs.
What happens if I set the limit too low by mistake?
Nothing breaks. The VM keeps running, just slower than it otherwise would be. Bump the number back up or remove it with qm set and it takes effect immediately, no restart required.
Is IOPS or MB/s the more important number to set?
Depends on the workload. Databases and anything doing lots of small random access care more about IOPS. Large sequential transfers — big file copies, media libraries, backup restores — care more about MB/s. If you're not sure which applies, set both at a level you're comfortable with.
Conclusion
Disk I/O limits aren't something you need on day one of running Proxmox, but the first time one VM's backup job or misbehaving app drags every other guest down with it, you'll wish you'd set them earlier. The settings themselves are simple — a handful of numbers on a disk line — the harder part is picking values that match what your storage can actually sustain with multiple VMs pulling from it at once. Start conservative, watch how things behave under real load, and loosen the numbers from there rather than the other way around.