Server Performance Tuning Checklist
A practical, hands-on checklist for tuning a Linux server: profile first to find the real bottleneck, then enable BBR, adjust sysctl and ulimit, tune Nginx and your database, add caching and a CDN — verifying each change as you go.
The first rule of tuning isn't "change a setting" — it's "measure first." The bottleneck on any server or VPS could be CPU, memory, disk IO, or the network, and blindly copying "magic" values from a forum often does nothing, or even makes things slower. Treat every item below as a small experiment: hypothesize, change, verify.
Step 1: Monitor first, find the real bottleneck
Before touching anything, see what the system is actually busy doing.
top # or htop — CPU, memory, load average
vmstat 1 # context switches, swap, IO wait
iostat -x 1 # disk IO utilization (%util near 100 means disk-bound)
ss -s # summary of socket/connection counts
If the CPU is idle but responses are slow, the bottleneck is usually disk or the database. If connections are maxed out, then look at network settings. Tuning without data is just guessing.
Step 2: Enable BBR congestion control
BBR noticeably improves throughput on high-latency, lossy links — ideal for cross-region traffic.
echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
sysctl net.ipv4.tcp_congestion_control # should print: bbr
Step 3: Tune sysctl kernel parameters
Append what you need to /etc/sysctl.conf, then apply with sudo sysctl -p:
- fs.file-max = 1000000 — system-wide maximum file handles
- net.core.somaxconn = 65535 — larger listen queue (essential under high concurrency)
- net.ipv4.tcpmaxsynbacklog = 65535 — SYN half-open queue
- net.ipv4.tcptwreuse = 1 — reuse sockets stuck in TIMEWAIT
- net.ipv4.iplocalportrange = 1024 65535 — widen the usable port range
Raise a value only once load testing shows that queue is actually overflowing — don't paste them all at once.
Step 4: Raise the ulimit open-file count
You can bump somaxconn all you want, but a low per-process nofile will still throw Too many open files.
# /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
For systemd-managed services, add LimitNOFILE=65535 to the unit file. Confirm with ulimit -n.
Step 5: Tune Nginx
worker_processes auto; # usually one per CPU core
worker_connections 10240; # concurrent connections per worker
keepalive_timeout 65; # reuse connections, cut handshakes
gzip on; # compress text responses
Run nginx -t to validate, then reload.
Step 6: Size the database buffer
The database is often the real bottleneck. Give its buffer enough memory — on a dedicated box, 50%–70% of physical RAM is a reasonable start: tune innodbbufferpoolsize for MySQL/MariaDB, or sharedbuffers and effectivecachesize for PostgreSQL. Watch the cache hit ratio afterward, and don't push memory so high that the box starts swapping.
Step 7: Add a cache layer and a CDN
At the application layer, put Redis or Memcached in front of hot queries and sessions to take load straight off the database. Hand static assets (images, JS, CSS) to a CDN — it both lightens the origin and speeds up access worldwide.
Summary
Tuning is a loop: monitor to find the bottleneck → change one thing → measure again to verify → keep it only if it helped. Move one variable at a time and record the metrics before and after. The right values depend on your machine's specs and your workload; there is no universal "optimal config." Hold that discipline, and your server or VPS will stay both fast and stable.