
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Slow servers rarely suffer from hardware failure; they usually suffer from default configurations that ignore your specific workload. Mastering Linux performance tuning basics means aligning kernel parameters, I/O schedulers, and memory policies with actual application behavior rather than relying on generic advice. Before you touch a single config file, establish a baseline using the observability techniques outlined in my guide to the four golden signals of monitoring, because optimizing without metrics is just guessing.
How do you apply Linux performance tuning basics safely with sysctl?
The sysctl interface exposes hundreds of kernel tunables, but changing them randomly causes instability. In practice, I maintain a dedicated configuration file at /etc/sysctl.d/99-performance.conf rather than editing the main /etc/sysctl.conf. This modular approach lets me version control changes per workload and roll back instantly by removing a single file. Always test parameters live with sysctl -w key=value before committing them to disk.
Network stack tuning for high-throughput services
Default Linux network buffers assume modest traffic. For web servers, reverse proxies, or API gateways handling thousands of concurrent connections, these defaults become bottlenecks. The following configuration increases socket buffer limits and enables TCP optimizations that reduce latency under load:
# /etc/sysctl.d/99-network-performance.conf
# Increase max socket buffer sizes (16MB)
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# Enable TCP BBR congestion control (kernel 4.9+)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# Increase connection tracking table size
net.netfilter.nf_conntrack_max = 262144
# Reduce TIME_WAIT accumulation for busy servers
net.ipv4.tcp_tw_reuse = 1 After applying, verify BBR is active with lsmod | grep bbr and sysctl net.ipv4.tcp_congestion_control. If BBR isn't available on older kernels, cubic remains a safe fallback. Never enable tcp_tw_recycle; it was removed in kernel 4.12 because it broke NAT environments.
Virtual memory and swap behavior
The vm.swappiness parameter controls how aggressively the kernel swaps anonymous pages versus reclaiming page cache. The default value of 60 works for desktops but harms database servers where cache retention matters more than keeping idle processes in RAM. For PostgreSQL or MySQL workloads, set this between 1 and 10. For stateless application servers, 30–60 is acceptable.
# /etc/sysctl.d/99-memory-tuning.conf
# Prefer keeping page cache over swapping (database servers)
vm.swappiness = 5
# Increase dirty page writeback thresholds for bulk writes
vm.dirty_ratio = 40
vm.dirty_background_ratio = 10
# Allow more open files system-wide
fs.file-max = 1048576
fs.nr_open = 1048576 A common mistake is setting vm.swappiness=0. This doesn't disable swap; it only avoids swapping until memory pressure becomes critical, then triggers aggressive reclamation that causes latency spikes. A low non-zero value provides a safety valve while still prioritizing cache. Pair this with proper swap file configuration to avoid OOM kills during transient spikes.
Which I/O scheduler should you use for different storage types?
The I/O scheduler determines how the kernel orders disk requests. Choosing wrong adds milliseconds of unnecessary latency per operation. Modern Linux offers four primary schedulers, each optimized for different hardware characteristics. Check your current scheduler with cat /sys/block/sda/queue/scheduler (replace sda with your device).
| Scheduler | Best For | Why It Works | When to Avoid |
|---|---|---|---|
| none | NVMe SSDs | Bypasses kernel scheduling; NVMe has internal parallelism and queue management | Rotational disks, SATA SSDs |
| mq-deadline | SATA SSDs, mixed workloads | Low overhead, guarantees read/write deadlines, multi-queue aware | Pure NVMe (use none), heavy sequential writes |
| bfq | Desktop, interactive workloads | Fair bandwidth allocation, responsive UI under load | Servers, high-IOPS databases |
| kyber | Fast NVMe, latency-sensitive apps | Token-based throttling, separates sync/async queues | Slow storage, rotational media |
To set the scheduler persistently, create a udev rule rather than relying on boot scripts that may race with device initialization:
# /etc/udev/rules.d/60-scheduler.rules
# NVMe devices: bypass kernel scheduling
ACTION=="add|change", KERNEL=="nvme[0-9]*n[0-9]*", ATTR{queue/scheduler}="none"
# SATA SSDs: use mq-deadline
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
# Rotational disks: use bfq for fairness
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq" Reload rules with sudo udevadm control --reload-rules && sudo udevadm trigger. Verify with cat /sys/block/nvme0n1/queue/scheduler; the active scheduler appears in brackets. On cloud VMs, the hypervisor often handles scheduling upstream; test before overriding, as double-scheduling increases latency.
How do you diagnose CPU and memory bottlenecks before tuning?
Tuning without diagnosis leads to solving problems you don't have. Before adjusting any parameter, spend 15 minutes collecting evidence. The perf tool reveals where CPU cycles actually go, distinguishing between application code, kernel overhead, and lock contention. Run perf top -g for a real-time profile, or perf record -a -g -- sleep 30 followed by perf report for detailed analysis.
- CPU saturation: Use
mpstat -P ALL 1to check per-core utilization. High %sys indicates kernel overhead (lock contention, excessive syscalls); high %iowait points to storage bottlenecks, not CPU shortage. - Memory pressure: Monitor
vmstat 1columnssi/so(swap in/out) andbi/bo(block I/O). Sustained swap activity with free memory suggests misconfigured swappiness or memory leaks. - Disk latency: Use
iostat -xz 1and focus onr_await/w_await(read/write latency in ms) and%util. Values above 10ms await or 80% util indicate saturation regardless of throughput. - Context switches: Excessive switching (>10K/sec/core) shows up in
vmstatcolumncs. Often caused by too many threads competing for few cores; reduce thread pools or pin CPUs.
For persistent monitoring, integrate these metrics into your Prometheus monitoring stack using node_exporter. Alert on sustained deviations from baseline rather than absolute thresholds; a database server at 90% CPU during batch processing is normal, but the same utilization during off-peak hours signals a problem.
What are common Linux performance tuning mistakes to avoid?
I've recovered more servers from bad tuning than from hardware failures. These anti-patterns appear repeatedly across teams and environments:
- Copying configs without understanding: A sysctl.conf optimized for a 256GB RAM database server will break a 4GB web container. Every parameter must be justified by your workload's resource profile and validated against your own metrics.
- Tuning multiple parameters simultaneously: Changing five settings at once makes it impossible to identify which helped, which hurt, and which had no effect. Change one variable, measure for 24+ hours, document results, then proceed.
- Ignoring application-level limits: Kernel tuning won't fix an unindexed query scanning millions of rows or a connection pool sized at 10 for 1000 concurrent users. Profile the application first; optimize the kernel second. See my MySQL performance tuning guide for database-specific patterns.
- Disabling security features for speed: Turning off ASLR, SELinux, or audit logging might yield marginal gains but violates compliance requirements and increases breach impact. Modern kernels minimize security overhead; benchmark before disabling anything.
- Forgetting persistence: Live
sysctl -wchanges vanish on reboot. Always write to/etc/sysctl.d/and test withsysctl -p /etc/sysctl.d/99-performance.confto catch syntax errors before restart.
Applying Linux Performance Tuning Basics in Production
Effective Linux performance tuning basics require discipline: measure first, change one variable at a time, validate with real traffic, and document every adjustment. Start with network buffers and I/O schedulers—they deliver the highest return with lowest risk. Reserve memory and CPU affinity tuning for cases where profiling proves a bottleneck. Automate configuration delivery through Ansible or Terraform to ensure consistency across environments and eliminate drift. If your team needs help establishing baselines or auditing existing configurations, reach out to discuss your infrastructure. Performance tuning isn't a one-time project; it's an ongoing practice embedded in your operational rhythm.