Skip to content
Back to Blog
Performance11 min read

Advanced Bare Metal vs VPS ROI: 8 Optimization Paths

Deep performance tuning and cost optimization strategies for bare metal and VPS infrastructure that go beyond basic provisioning decisions.

Written by Abdul AbrorTechnical Hosting Support Engineer
Advanced Bare Metal vs VPS ROI: 8 Optimization Paths
On this page

The provisioning choice between bare metal and VPS matters less than what you do after the server boots. I've seen teams spend weeks debating hardware specs while leaving default kernel parameters and stock network configs untouched. That's where real ROI lives.

This isn't about baseline comparisons. You already know bare metal gives you dedicated resources and VPS offers flexible scaling. What follows are eight optimization paths that change the economics and performance profile of both deployment types once you move past the sales brochure.

Path 1: NUMA topology awareness

Modern multi-socket systems and some high-core-count VPS instances expose NUMA domains. Default Linux memory allocation is NUMA-aware but application placement often isn't.

Check your topology first:

numactl --hardware
lscpu | grep NUMA

If you see multiple nodes, pin memory-intensive processes to specific NUMA nodes where their memory resides. A database server accessing memory across NUMA boundaries pays a 40-60% latency penalty on every remote access.

For bare metal PostgreSQL:

numactl --cpunodebind=0 --membind=0 postgres -D /var/lib/postgresql/data

VPS platforms sometimes expose NUMA even on fractional CPU allocations. I've caught this on large Hetzner and OVH instances. Your numastat output will show imbalanced memory access patterns if you're hitting cross-node traffic.

Set vm.zone_reclaim_mode=0 in /etc/sysctl.conf to prefer remote memory over local page cache eviction. Sounds backwards but prevents thrashing when a node's memory fills.

Path 2: Interrupt affinity and RPS tuning

Network interrupt handling defaults are terrible. A single CPU handles all interrupts for a NIC queue, creating a bottleneck at 400-600K packets per second. Every support ticket I've handled for "the network is slow but bandwidth is fine" traces back to this.

Identify your NIC's IRQ numbers:

grep eth0 /proc/interrupts

Spread interrupts across cores manually. For a quad-core system with eth0 using IRQs 50-53:

echo 1 > /proc/irq/50/smp_affinity
echo 2 > /proc/irq/51/smp_affinity
echo 4 > /proc/irq/52/smp_affinity
echo 8 > /proc/irq/53/smp_affinity

Those are hexadecimal bitmasks. CPU 0 = 1, CPU 1 = 2, CPU 2 = 4, CPU 3 = 8.

Enable RPS (Receive Packet Steering) to distribute packet processing beyond interrupt handling:

for f in /sys/class/net/eth0/queues/rx-*/rps_cpus; do
    echo f > $f
done

Bare metal gives you full control here. VPS platforms occasionally block /proc/irq writes, but RPS configuration works universally because it's a software queue.

Path 3: Transparent Huge Pages — know when to disable

THP sounds great on paper. Automatic 2MB pages instead of 4KB. Except memory-intensive applications with sparse access patterns take a massive hit from THP defragmentation.

Redis, MongoDB, and most Java workloads perform worse with THP enabled. The kernel's defrag thread (khugepaged) stalls allocations.

Check current setting:

cat /sys/kernel/mm/transparent_hugepage/enabled

If you see [always], you're probably paying a tax. Disable it:

echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

Make it permanent via /etc/rc.local or a systemd unit. I've measured 15-20% latency improvements on Redis after disabling THP under write-heavy loads.

VPS platforms sometimes lock this setting. If /sys/kernel/mm/transparent_hugepage doesn't exist, you're on an older kernel or a heavily restricted VPS. That's a real constraint.

Path 4: Storage scheduler and queue depth

The default I/O scheduler (mq-deadline on modern kernels) optimizes for rotational disks. Your NVMe or SSD doesn't need that.

Check what's active:

cat /sys/block/nvme0n1/queue/scheduler

Switch to none for NVMe or kyber for SATA SSDs:

echo none > /sys/block/nvme0n1/queue/scheduler

Queue depth matters more than scheduler choice. Default nr_requests is 128, which starves high-IOPS devices.

echo 1024 > /sys/block/nvme0n1/queue/nr_requests

Bare metal gives you direct NVMe access. VPS block devices are often virtio-blk with a hypervisor queue in between. You can still tune the guest-side queue, but you're optimizing one side of a two-layer system.

For VPS, test different noop vs mq-deadline settings under your actual workload. I've seen counterintuitive results where mq-deadline performed better on virtio-blk because the hypervisor's own scheduler expected batched requests.

Path 5: CPU governor and frequency scaling

Servers ship with powersave governor by default. Your CPU idles at minimum frequency until load spikes, then ramps up over 50-100ms. That ramp delay kills request latency.

Check current governor:

cpupower frequency-info

Switch to performance:

cpupower frequency-set -g performance

Disable C-states for the lowest-latency path:

for i in /sys/devices/system/cpu/cpu*/cpuidle/state*/disable; do
    echo 1 > $i
done

This costs you 10-15W per CPU at idle. For bare metal, that's real power draw. Calculate whether latency savings justify the electricity cost. At $0.10/kWh, 15W is about $13/year per server.

VPS instances sometimes expose governor controls, sometimes don't. If cpupower commands fail with permission errors, you're locked to the hypervisor's choice. That's a hidden architectural difference that impacts tail latency.

Path 6: Network stack buffer tuning

Default TCP buffers are sized for 1 Gbps links with moderate latency. If you're on 10 Gbps or higher, you're leaving bandwidth on the table.

Check current values:

sysctl net.ipv4.tcp_rmem
sysctl net.ipv4.tcp_wmem

Increase them:

sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216

Enable TCP window scaling and timestamps if they're off:

sysctl -w net.ipv4.tcp_window_scaling=1
sysctl -w net.ipv4.tcp_timestamps=1

Bare metal and VPS both respond to these settings, but VPS network performance has a ceiling imposed by the hypervisor's virtual NIC throughput. I've seen VPS instances advertised at 10 Gbps that couldn't push more than 4 Gbps in practice due to virtio-net limitations.

Test real throughput with iperf3 against a known-good target before assuming your tuning worked.

Path 7: Filesystem mount options and I/O patterns

Ext4 and XFS defaults prioritize data safety over performance. That's correct for most workloads, but if you're already replicating data or can tolerate a crash losing seconds of writes, you can trade safety for speed.

For ext4 on a database server with WAL-based durability:

mount -o remount,noatime,nodiratime,data=writeback /dev/nvme0n1p1 /var/lib/mysql

data=writeback skips ordered mode. Metadata updates don't wait for data writes. A crash can corrupt files, but your database's own WAL protects against logical corruption.

XFS users should try:

mount -o remount,noatime,nodiratime,logbufs=8,logbsize=256k /dev/nvme0n1p1 /data

Larger log buffers reduce write latency for metadata-heavy operations. I've measured 20-30% improvement on workloads creating thousands of small files per second.

VPS platforms sometimes restrict mount options via virtualized block devices. If you get permission errors remounting, check dmesg for capability restrictions.

Path 8: Real cost modeling with failure domains

The ROI calculation everyone skips: failure domain cost. Bare metal fails at the server level. VPS fails at the hypervisor level, taking down multiple guests.

If you're running three VPS instances for redundancy, check whether your provider isolates them across physical hosts. Most don't by default. I've seen entire customer stacks go dark when a single hypervisor died because all three VPS instances were on the same box.

For bare metal, your failure domain is the server. Calculate replacement cost, not just monthly fees:

  • Server fails, you pay shipping and wait 24-48 hours for replacement in most DCs.
  • VPS fails, provider migrates you to new hypervisor in 5-15 minutes.

But if your VPS provider has a hypervisor die and loses customer data (happened to OVH in 2021, others before), you don't get the hardware-level redundancy options bare metal gives you: RAID, dual PSUs, IPMI for remote recovery.

Build a real cost model:

Bare metal annual cost = (monthly fee × 12) + (failure_rate × replacement_cost × downtime_hours × hourly_revenue_loss)
VPS annual cost = (monthly fee × 12) + (hypervisor_failure_rate × data_loss_penalty)

Failure rates vary by provider, but typical numbers: bare metal server ~3-5% annual failure, VPS hypervisor ~1-2% annual failure. Your downtime cost and data loss penalty are specific to your business.

In support tickets where teams asked me to justify their infrastructure choice, the ones who'd actually modeled failure costs had made defensible decisions. The ones arguing about CPU benchmarks had not.

Start with observable bottlenecks

Don't tune blindly. Run your workload, watch mpstat, iostat, and sar -n DEV for 24 hours. Identify where your system actually spends time waiting.

If CPU is pegged, look at governors and interrupt affinity. If disk await times are high, check I/O scheduler and queue depth. Network drops in ethtool -S mean you need interrupt tuning.

The ROI of bare metal versus VPS isn't in the hardware spec sheet. It's in whether you extract what the hardware can actually deliver.

FAQ

Does NUMA tuning matter on single-socket bare metal?

No. If numactl --hardware shows one node, skip it. NUMA overhead only appears on multi-socket or high-core-count systems where memory is distributed.

Can I tune interrupt affinity on Vultr or DigitalOcean VPS?

Sometimes. Smaller instances usually block /proc/irq writes. Larger instances (8+ vCPUs) sometimes expose it. Test with echo 1 > /proc/irq/50/smp_affinity and check dmesg for permission errors.

What's the first tuning step for a new server?

Disable THP if you're running a database or cache. It's the highest-impact, lowest-risk change. Second step: switch NVMe scheduler to none.

How do I know if my VPS is actually on separate hypervisors?

You don't, unless your provider exposes hypervisor IDs via metadata APIs or you ask support. AWS and GCP let you specify placement groups to force separation. Most smaller providers don't.

Does bare metal beat VPS for every workload?

No. Bursty workloads that idle most of the time pay too much for bare metal. Steady, predictable workloads with high resource utilization favor bare metal. Calculate your utilization percentage first.