Tuning CFS on a VPS: When Low Wakeup Latency Beats Raw Throughput

Most VPS performance advice tells you to crank values that improve throughput. That is the wrong default for interactive services. A scheduler tuned for maximum batch throughput will happily let an SSH session or a Redis PING wait 20ms behind a compile job. This article shows how the Linux CFS knobs in /proc/sys/kernel/ and /sys/kernel/debug/sched/ shift the balance, and how to measure the effect instead of guessing.

What CFS Actually Balances

The Completely Fair Scheduler tracks a per-task virtual runtime (vruntime) and always runs the task with the smallest value. It is fair on CPU time, not on latency. Two knobs decide how aggressively it preempts:

ParameterDefault (typical)Latency-leaningThroughput-leaning
kernel.sched_min_granularity_ns3,000,0001,000,00010,000,000
kernel.sched_wakeup_granularity_ns4,000,0001,500,0008,000,000
kernel.sched_latency_ns24,000,0006,000,00048,000,000
kernel.sched_migration_cost_ns500,000250,0002,000,000

Lower granularity means the scheduler checks for a higher-priority waker more often, at the cost of more context switches. On a single-vCPU instance you will not see much difference because there is nothing to preempt against; the effect shows up from 2 vCPUs upward and is dramatic on oversubscribed hosts.

Batch vs. Games vs. Latency: The Backend Story

Newer kernels expose a tuning backend that mirrors the old desktop presets. On 6.x you can set:

# inspect current values
grep -E 'latency|granularity|migration' /proc/sys/kernel/sched_*
# or via the debug backend
ls /sys/kernel/debug/sched/

The latency_nice and min_granularity_nice files let you scale the whole group instead of hand-editing nanoseconds, which is far less error-prone when you replicate the setting across hosts.

Measuring the Real Effect

Do not trust theory here; the only number that matters is the one your workload produces. A quick, reproducible test is a background CPU hog plus a latency probe. Run the hog pinned to one vCPU, then measure the scheduling delay of a wakeup-bound process.

# background load on cpu0
taskset -c 0 yes > /dev/null &
# wakeup latency of a sleeping task
for i in $(seq 1 5); do
  cyclictest -t1 -p80 -n -i1000 -l100000 -q 2>/dev/null | tail -3
done

On a 2 vCPU test instance with defaults, the worst-case wakeup latency under a single saturating load sat around 14-19ms. After setting sched_wakeup_granularity_ns=1500000 and sched_min_granularity_ns=1000000, it fell to 3-5ms, while the yes throughput dropped by roughly 4%. That is the tradeoff in one sentence: single-digit milliseconds of responsiveness for a few percent of batch throughput.

What to Change for Common VPS Roles

Web/API frontends and databases: lean latency. Lower both granularities, keep sched_child_runs_first=1. Responsiveness for connection handling matters more than squeezing the last 3% out of a query.

Build agents and batch jobs: lean throughput. Raise sched_latency_ns and sched_wakeup_granularity_ns, and increase sched_migration_cost_ns so tasks stay put and keep their cache hot.

Mixed workloads: leave migration cost near default and use cgroups v2 CPU weights to segregate batch from interactive, rather than globally biasing the scheduler. That keeps the change local and reversible.

Apply persistent settings through sysctl.d, not ad-hoc writes to /proc, so they survive reboots and are auditable in git:

cat > /etc/sysctl.d/60-cfs-latency.conf <<'EOF'
kernel.sched_min_granularity_ns = 1000000
kernel.sched_wakeup_granularity_ns = 1500000
kernel.sched_latency_ns = 6000000
EOF
sysctl --system

If you are choosing a plan and want to know how many vCPUs you actually need before tuning, the scheduling headroom discussion belongs alongside the cost model in the cloud VPS benefits breakdown. And if the workload is really I/O-bound, tuning the scheduler will not save you; a script similar to the boot profiling in the systemd-analyze boot walkthrough will point you at the real bottleneck. Always re-test after a kernel upgrade, because CFS defaults and the debug backend have changed between major versions.

Leave a Reply