Balancing Performance and VM Density: A Practical Approach
When first setting up a Proxmox lab, many often fall into the “better safe than sorry” trap. We tend to over-allocate resources to virtual machines (VMs). However, assigning too many vCPUs or RAM doesn’t make a VM faster. On the contrary, it can drag down the performance of the entire Hypervisor due to resource contention.
I am currently running a homelab cluster with 12 various VMs and containers. Based on my experience dealing with nodes freezing due to Disk I/O congestion, I’ve learned that understanding allocation mechanisms is the key to keeping a system running 24/7. You don’t need beastly hardware; you just need smart configuration.
Over-provisioning vs. Fixed Allocation: Which Side to Choose?
In virtualization, there are two resource management philosophies you need to distinguish clearly:
1. Over-provisioning
This involves assigning total VM resources that exceed the physical capacity of the host. For example: A server has only 32GB of RAM, but you assign 4GB to each of 10 VMs (totaling 40GB).
- Pros: Maximizes hardware utilization and is highly cost-effective.
- Risks: If all VMs peak simultaneously, the Hypervisor will run out of RAM, leading to constant swapping or system crashes.
2. Fixed Allocation
Each VM holds a distinct portion of resources. The Hypervisor “locks” that portion, preventing others from touching it.
- Pros: Absolute performance stability with no resource contention.
- Cons: Wasteful if the VM is idling at 5-10% load.
My experience: For CPUs, you can comfortably over-provision at a 1:3 ratio (1 physical core assigned to 3 vCPUs). However, be extremely cautious with RAM. Ballooning should only be used in test environments.
CPU Optimization: Don’t Let vCPUs Become a Burden
Assigning 8 vCPUs to a VM on a host with only 4 physical cores is a fundamental mistake. This causes constant Context Switching. The CPU wastes cycles switching between tasks, causing latency to skyrocket.
Switch to CPU Type “Host” Immediately
By default, Proxmox uses kvm64 to ensure you can live-migrate VMs to older servers. However, kvm64 hides important instruction sets like AES-NI or AVX. If you don’t plan on running a cluster with mixed CPU generations, switch to host to leverage 100% of the actual chip’s power while optimizing virtualization performance.
# Force VM ID 100 to use physical CPU instruction sets
qm set 100 --cpu host
The Power of NUMA and CPU Pinning
For Dual Socket servers, enable NUMA (Non-Uniform Memory Access). It allows the VM to recognize which RAM stick is physically closer to which CPU, minimizing data access latency across the system bus.
If running large databases, consider using CPU Pinning. This technique binds a vCPU to a specific physical core, ensuring data stays in the L1/L2 cache and significantly boosting processing speed.
RAM Management: The Art of Using Ballooning
How Does Memory Ballooning Work?
Imagine Ballooning like a balloon inside the VM’s stomach. When the Hypervisor runs low on RAM, it inflates this balloon to take up space, forcing the VM to release RAM back to the Host. For this feature to work, you must install the qemu-guest-agent.
# Install the agent on Ubuntu/Debian
sudo apt update && sudo apt install qemu-guest-agent -y
sudo systemctl enable --now qemu-guest-agent
Warning: Never use Ballooning for MySQL, PostgreSQL, or Redis. Databases always want to cache everything in RAM. If RAM is suddenly reclaimed, the service will freeze or crash immediately.
Disk I/O: Handling the Most Frustrating Bottleneck
In virtualization, Disk I/O is often the first bottleneck. When the IO Wait metric exceeds 10%, every operation on the VM will feel “laggy” even if the CPU is idle.
Enable VirtIO SCSI Single and IO Thread
Don’t use the default drivers. Optimizing KVM Virtual Machine Performance requires choosing VirtIO SCSI Single so that each virtual disk has its own queue. Specifically, enable IO Thread. This option allows read/write operations to run on a dedicated CPU thread, preventing them from blocking the VM’s main processing thread.
# Example of optimized configuration in /etc/pve/qemu-server/100.conf
scsihw: virtio-scsi-single
virtio0: local-lvm:vm-100-disk-0,iothread=1,discard=on
Rate Limiting to Avoid the “Noisy Neighbor” Problem
A VM running a backup can saturate the disk bandwidth, causing other VMs to stall. I usually limit the speed for non-priority VMs to 50MB/s or 100MB/s to ensure fairness.
# Limit VM 100 to a maximum read/write of 50MB/s
qm set 100 --virtio0 local-lvm:vm-100-disk-0,bwlimits=read=50,write=50
Standard Optimization Workflow
To keep the Hypervisor running smoothly, I always follow these 4 steps:
- Monitoring: Use
htopandiostat -x 1to identify which VM is causing the bottleneck. - CPU: Always select
Type: Host. The vCPU:pCPU ratio should not exceed 4:1 for light workloads. - RAM: Use fixed allocation for Databases. Only use Ballooning for web servers or proxies.
- Disk: Prioritize SSD/NVMe, use
VirtIO SCSI Single, and always enableDiscardto free up unused space.
Resource optimization is a journey of continuous fine-tuning. Hopefully, these practical tips will help you master your KVM/Proxmox beast.

