Random tuning tips from the internet rarely help. A repeatable method does: measure, find the bottleneck, fix that one thing, measure again. This guide follows that approach for a typical Linux web or application server.
1. Start with the symptom
Define what “slow” means in numbers: page response time, API latency, job duration, requests per second. Measure it before changing anything:
curl -o /dev/null -s -w 'connect %{time_connect}s, ttfb %{time_starttransfer}s, total %{time_total}s\n' https://example.com/
2. Check resources with the USE method
For every resource, check Utilization, Saturation and Errors:
| Resource | Utilization | Saturation | Errors |
|---|---|---|---|
| CPU | mpstat -P ALL 1 |
load average vs nproc, run queue (vmstat r) |
— |
| Memory | free -h (available) |
swapping (vmstat si/so), OOM kills |
journalctl -k | grep -i oom |
| Disk | iostat -xz 1 (%util) |
aqu-sz, high await |
dmesg | grep -i error |
| Network | sar -n DEV 1 |
dropped packets, retransmits (nstat) |
ip -s link |
Pressure stall information summarizes saturation neatly:
cat /proc/pressure/{cpu,memory,io}
3. Fix the bottleneck you found
CPU-bound
- Find the process:
top,pidstat 1. - Profile the application (slow code paths, heavy regex, missing caching).
- For web apps, cache rendered pages so most requests skip application code.
- Check
%st(steal) intopon a VPS — high steal means the host is busy; consider a larger plan.
Memory-bound
- Reduce worker counts so everything fits in RAM (PHP-FPM, Gunicorn, Node cluster workers).
- Find memory hogs:
ps aux --sort=-rss | head. - Add swap as a safety net, not a solution — see swap file vs partition.
Disk-bound
- Identify the writer/reader with
iotop. - Give databases enough buffer memory, add Redis caching, reduce logging.
- Details: Linux disk performance.
Network-bound
- Enable compression (gzip/Brotli) and HTTP/2 or HTTP/3.
- Use a CDN for static files — see VPS + Cloudflare.
- Enable BBR congestion control for distant users.
- Check bandwidth limits: VPS bandwidth explained.
4. Tune the services
Most real-world wins come from service configuration rather than the kernel:
- Nginx:
worker_processes auto, enoughworker_connections, keepalive, static file caching,open_file_cache. - PHP-FPM: OPcache on,
pm.max_childrensized to RAM. - MariaDB/MySQL:
innodb_buffer_pool_size, slow query log, indexes on filtered columns. - PostgreSQL:
shared_buffers,effective_cache_size,work_mem; useEXPLAIN ANALYZEon slow queries.
5. Kernel and system settings (when needed)
Only after the above, and only for the bottleneck you see:
# /etc/sysctl.d/90-perf.conf
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.ip_local_port_range = 10240 65535
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
vm.swappiness = 10
Raise file-descriptor limits for busy services (LimitNOFILE in a systemd override). More in Debian performance tuning.
6. Measure again — and keep measuring
Repeat your original measurement. Keep long-term monitoring (Netdata, Prometheus + node_exporter + Grafana) so you can see trends and correlate slowdowns with deploys or traffic.
7. Know when to scale
Tuning has limits. Scale up when resources are consistently above ~70% at normal peak load after optimization. Options:
- Vertical: more vCPU/RAM — our KVM VPS plans can be upgraded by ticket.
- Horizontal: separate the database onto its own server, add a load balancer and more app servers.
- Dedicated hardware for consistently heavy workloads — see dedicated servers.
Summary
Measure the symptom, find the saturated resource, fix that, and measure again. Most gains come from caching and service configuration; kernel tuning is the finishing touch.