Transcription
All right, let's jump right into this explainer and solve a seriously frustrating mystery. Why is your internal VM to VM traffic crawling?
Look, if you run a home lab or manage virtual servers, you know exactly the kind of headache I'm talking about. Every single piece of your setup looks faster than the actual number you're seeing on the screen. You're staring at the network cards, you're double-checking the VM settings, and you're just praying the next speed test magically behaves. Well, today we're breaking down exactly why this bottleneck happens in environments like Proxmox, and more importantly, how we're going to fix it.
So, picture this scenario. On paper, your server has absolutely all the right ingredients. A 1 gigabit network card, a 2.5 gigabit card, maybe even a beefy 5 gig one. You've got an OpenSense router VM and a Windows 11 VM sitting nicely right behind it. Your wide area network is out there stretching its legs, pulling a massive 4 gigabits per second through that 5 gig interface. Fantastic, right? But then, you go to test your Windows VM sitting on that 2.5 gigabit bridge, and your internal speed test absolutely refuses to climb past 2.5 gigabits per second. Boom, it just hits a hard, immovable ceiling. And man, this is exactly where the headache starts.
I mean, the entire promise, literally the whole point of virtualization, is that some of your network traffic never actually needs to leave the host machine, right? If one VM is talking to another VM inside the exact same Proxmox box, shouldn't that traffic move at blistering internal motherboard speeds? Why on earth would your packets care about the physical limits of a hardware network card if they are just bouncing around inside the same box? It honestly feels exactly like finding a hidden toll booth restricting traffic inside your own private hallway. It's crazy.
You move the Windows VM to the 1 gigabit bridge, and boom, the speed drops to 1 gigabit. You move it back, and it follows that 2.5 gigabit ceiling perfectly. It makes you sit back and ask, wait, is this just how Linux bridges actually work, or did I configure something horribly wrong? Well, the answer, as is wonderfully typical with virtual networking, is both simpler and messier than you might expect.
Okay, to solve this mystery, we first need to do a quick sanity check and clean up some basic assumptions. Let's start with a lowercase b because, you know, gigabits and gigabytes are definitely not the same animal. When you're debugging performance, mixing these up is easily the most common trap. A lowercase b means bits and an uppercase b means bytes. Mixing them up can make your performance expectations go completely sideways.
But even after we verify our units are spot-on, we still have this massive discrepancy between what the VM reports and what it actually delivers. See, that 10 gigabit link speed you're seeing in Windows? That number is often more like a label on a virtual lane than a measured speed limit. It's just telling the guest what kind of virtual NIC it thinks it has. It is absolutely not promising real-world throughput. Seeing 10 Gbps inside a guest OS is incredibly tempting, I know. It looks like hard proof, like a binding contract from your hypervisor. But a virtio adapter reporting 10 gigabits per second is simply a label. It absolutely does not tell you whether the host CPU can push packets that fast or if a single network flow is stuck waiting in line.
So, the core of our investigation brings us to the Linux bridge. Now, it sounds like one simple, straightforward thing, but it actually plays very different roles depending on how it's connected. We naturally tend to think of a bridge as a private, abstract tunnel between our VMs. But here's the kicker. A bridge tied to a physical network card is not entirely private. It has a real uplink to the outside world and guest VMs will often honor the limits of whatever physical hardware that bridge is anchored to. Think about it this way. If your Windows VM and your Open Sense router share a bridge that is tied to a 2.5 gigabit PS physical NIC, your traffic essentially has one foot outside the box. Even if the traffic you're testing is strictly going from one VM to another, if it crosses a bridge anchored to the outside world, it becomes bound by the boring physics of that physical doorway. It's not some philosophical software answer, it is literally just the physical limits of the network path you've built.
But let's say the traffic is completely internal on a truly private switch. Then we have to investigate a whole new lineup of suspects that can drag down your multi-gig throughput. If it's not the physical network card dragging you down, what is it? Well, it could be host CPU performance limits, VirtIO driver behavior, VM scheduling, single thread bottlenecks at 10 gigabits. We also have to consider the heavy lifting done by your router VM or even storage speed limits if your tests are actually writing data to a physical disk. And we definitely can't ignore the hidden cost of your firewall. Touching packets and applying rules might feel totally invisible at 1 gigabits, but man, once you hit 5 gigabits or more, it starts sending heavy CPU invoices. Firewalls aren't just simple routers, right? They track state, they apply complex rules, they handle network address translation. Ask a small mini PC to do all of that at multi-gigabit speeds and you are going to hit a CPU bottleneck fast. Turn on intrusion detection and your throughput can fall even harder.
So, to figure out exactly which of these suspects is guilty, you really need a careful testing mindset. You can't just run one single speed test through a router VM and call it a day because that blurs way too many variables together. You have to strip the path down. Use tools like iPerf directly between two Windows VMs to completely remove the router from the story. Check your storage speeds with CrystalDiskMark. You want to isolate the network flow to see if the bottleneck is the firewall, the OS, or the host hypervisor itself.
Okay, so how do we actually fix this? Good news, it's a surprisingly clean three-step process to redesign your network. And spoiler alert, the solution isn't buying a newer, more expensive network card. It isn't wiping and reinstalling Proxmox, and it definitely isn't randomly clicking offload toggles in the UI for hours on end hoping for a miracle. The real fix comes down to clarifying your network design to shed that physical baggage.
Step one, create an unbound bridge. By creating a bridge with absolutely no physical uplink attached to it, your traffic finally stops pretending to be on a physical adapter. Instead, it rides a true host-only virtual switch. You just create a brand new network bridge in Proxmox and deliberately do not link it to any physical NIC. Then attach your internal VMs directly to that unbound bridge. Suddenly, the setup perfectly matches your mental model. The traffic is truly 100% internal.
Now, the second half of this fix is setting your VirtIO multi-queue count to match your VM's virtual CPU setup. Removing the physical NIC gets rid of that hard ceiling we talked about, but VirtIO networking can still be heavily bottlenecked by CPU queue behavior. Virtual networking software relies on queues to pass data. If you don't configure this to match your core count, say setting it to four if you have four VCPUs, you're leaving a massive amount of performance on the table.
So, why does this multi-queue setting actually matter? Because at multi-gig speeds, a single connection can get painfully bottlenecked on just one CPU thread. Multi-queue allows the VM to move packets across multiple queues simultaneously. By enabling it and making sure you don't set the queue count higher than the VM's allocated VCPU count, you distribute that intense processing load. Look, it doesn't magically turn a mini PC into an enterprise data center switch, but it definitely gives your processor the room it needs to breathe.
Really, fixing this bottleneck is all about fixing your mental model. You have to replace the illusion of the user interface with an actual understanding of how virtual switches are designed. The Proxmox UI gives you bridges, NIC models, checkboxes, and then the guest OS confidently reports 10 gigabits per second. If your brain assumes an attached bridge is a private tunnel, the whole system just feels broken. You just have to remember this golden rule. Use physical bridges for physical networks and unbound bridges for private internal networks.
And hey, keep in mind you can ask a small box to route, NAT, and test multi-gig flows all at once, but it will politely remind you that underlying silicon still has to do the heavy lifting. Even with a perfectly configured unbound bridge and multi-Q enabled, there is no such thing as infinite networking. You still need enough CPU cycles and a clean traffic path to actually process those gigabits of data. Virtual networking is software, yes, for sure, but it is ultimately executed by your physical processor.
Ultimately, it is just incredibly satisfying to finally give this bottleneck a name and fix it. Once you stop confusing a local area network bridge with an internal backplane, your network stops feeling haunted. You're no longer chained to the physical link speed for internal traffic. Which leaves us with one final intriguing question for your own home lab. Take a look at your virtual infrastructure map. Now that this hidden tollbooth is gone, what else in your setup is pretending to be something it's not?