Linux VPS Performance Tuning: sysctl & swap 2026

A small VPS — 1 or 2 GB of RAM, a couple of vCPUs — ships with kernel defaults tuned for a generic desktop or a large server, neither of which describes your box. A handful of sysctl and limit adjustments can make that same hardware noticeably more responsive under load: less needless swapping, more simultaneous connections, and headroom for the file descriptors a busy web server burns through. This guide covers the tuning that actually moves the needle on a small Linux VPS, how to apply each change so it survives a reboot, and how to confirm it took effect.

Quick Verdict
The tuning with the best return on a small VPS is lowering vm.swappiness so the kernel stops swapping out active memory prematurely, raising the open-file limits so a busy server does not hit "too many open files," and widening the network connection backlog. Apply everything through a file in /etc/sysctl.d/ rather than ad-hoc commands, and verify each value with sysctl after a reboot — an un-persisted tweak silently reverts.

Profile Before You Tune

Tuning blind wastes effort. Spend two minutes seeing where the server actually spends its time. Check memory and swap pressure:

1free -h
2vmstat 1 5

In vmstat, watch the si and so columns — sustained nonzero swap-in/swap-out under normal load means the kernel is paging active memory to disk, which is slow and a prime tuning target. Check current limits and load:

1ulimit -n
2uptime

ulimit -n shows the per-process open-file ceiling; the common default of 1024 is low for a web or database server. Knowing your starting numbers lets you confirm the changes below did something real.


Tune Memory Behavior with swappiness

vm.swappiness controls how eagerly the kernel moves memory to swap, on a scale of 0 to 100. The default of 60 is aggressive for a server — it swaps out application memory long before RAM is actually exhausted, trading fast memory access for slow disk. On a VPS with real workloads, lower it:

1sudo sysctl vm.swappiness=10

A value of 10 tells the kernel to keep processes in RAM and only swap when memory is genuinely tight. This is one of the most noticeable changes on a memory-constrained box — applications stay resident and responsive instead of being paged out during quiet moments.

A companion setting reduces how aggressively the kernel reclaims cached filesystem metadata:

1sudo sysctl vm.vfs_cache_pressure=50

These commands change the live value immediately but do not persist; we make them permanent in the final step.


Raise File Descriptor Limits

Every open connection, file, and socket consumes a file descriptor. A busy Nginx or database server can exhaust the default 1024 limit and start logging "too many open files" — requests fail even though CPU and memory are idle. Raise the system-wide maximum:

1sudo sysctl fs.file-max=200000

The per-process limit is separate and lives in /etc/security/limits.conf. Add soft and hard limits for your service user:

1*    soft    nofile    65535
2*    hard    nofile    65535

For a service managed by systemd, the cleaner approach is to set it directly in the unit with a drop-in:

1[Service]
2LimitNOFILE=65535

After a re-login or service restart, ulimit -n should report the new ceiling.


Widen the Network Stack

Default network buffers and queues are sized conservatively. For a server handling many short-lived connections, three sysctl values matter most:

1sudo sysctl net.core.somaxconn=1024
2sudo sysctl net.ipv4.tcp_max_syn_backlog=2048
3sudo sysctl net.core.netdev_max_backlog=2000

net.core.somaxconn is the ceiling on the listen backlog — how many established connections can queue waiting for your application to accept them. The default of 128 is easily overrun by a busy web server, causing dropped connections under burst traffic; 1024 gives real headroom. The two backlog values widen the queues for incoming SYN packets and packets waiting to be processed, smoothing out traffic spikes.

A further tweak lets the kernel reuse connections in the TIME_WAIT state more readily, useful on servers making many outbound connections such as reverse proxies:

1sudo sysctl net.ipv4.tcp_tw_reuse=1

Pick the Right I/O Scheduler

On a VPS backed by NVMe or SSD storage, the traditional rotational-disk schedulers add latency for no benefit. Check what your disk uses — the value in brackets is active:

1cat /sys/block/sda/queue/scheduler

For solid-state storage, none (also called noop) or mq-deadline is appropriate — they avoid the elevator reordering that only helps spinning platters. Many cloud images already default to none on virtual disks, so check before changing. If needed, set it temporarily:

1echo none | sudo tee /sys/block/sda/queue/scheduler

Making this persistent requires a udev rule or kernel boot parameter, since /sys resets on reboot.


Make It Permanent and Verify

Everything applied with sysctl on the command line vanishes at reboot. Persist your choices in a dedicated file under /etc/sysctl.d/ so they load on every boot and stay separate from the package defaults:

1sudo nano /etc/sysctl.d/99-vps-tuning.conf
1vm.swappiness = 10
2vm.vfs_cache_pressure = 50
3fs.file-max = 200000
4net.core.somaxconn = 1024
5net.ipv4.tcp_max_syn_backlog = 2048
6net.core.netdev_max_backlog = 2000
7net.ipv4.tcp_tw_reuse = 1

Load the file without rebooting:

1sudo sysctl --system

Then verify each value actually took — never assume:

1sysctl vm.swappiness net.core.somaxconn fs.file-max

The reported values should match your file. Re-run free -h and vmstat 1 5 under load and compare to your baseline; with swappiness lowered, the swap columns should stay quiet where they previously showed activity.


Tune for Your Workload, Then Confirm

These values are sound defaults for a small general-purpose VPS, but the principle matters more than any single number: profile first, change one thing at a time, persist it in /etc/sysctl.d/, and verify the new value with sysctl after a reboot. A tweak you applied once on the command line and never persisted is a tweak that quietly disappeared the next time the server restarted.

Lower swappiness, lift the file-descriptor ceiling, widen the connection backlog, and match the I/O scheduler to your storage. None of it costs money or a bigger plan — it is the same hardware, configured to suit the work you actually put it to.

Posts in this series