How to Speed Up Your VPS: Caching, Swap, and Kernel Tuning for Maximum Performance

How to Speed Up Your VPS: Caching, Swap, and Kernel Tuning for Maximum Performance

A $20/month VPS with the right configuration often outperforms a $100/month VPS with default settings. Most server performance problems are not hardware problems — they’re configuration problems. Nginx serving uncompressed files, PHP-FPM running with default pool sizes, a kernel not using Google’s BBR algorithm, no swap space for memory overflow — these are all common default states that leave significant performance on the table.

This guide walks through the most impactful VPS performance optimizations in 2025: caching at multiple layers, swap configuration, TCP/kernel tuning, PHP-FPM optimization, and database tuning — all with copy-paste commands for Ubuntu 22.04/24.04.

Before You Optimize: Establish a Baseline

Optimization without measurement is guesswork. Before making any changes, record your current performance metrics so you can quantify the improvement each change delivers.

# CPU and load average
top -bn1 | head -5

# Memory usage
free -h

# Disk I/O performance (install if needed: sudo apt install fio -y)
fio --name=randread --ioengine=libaio --iodepth=16 --rw=randread --bs=4k --size=100M --numjobs=4 --runtime=30 --group_reporting

# Web server benchmark (install if needed: sudo apt install apache2-utils -y)
ab -n 10000 -c 100 http://localhost/

# Current sysctl settings
sysctl -a | grep -E "tcp_congestion|net.core|vm.swappiness"

Save these numbers. After each optimization, re-run the relevant tests to measure actual improvement.


Part 1: Nginx Performance Tuning

Nginx’s default configuration is intentionally conservative. These changes unlock its full potential for production workloads.

Worker Processes and Connections

sudo nano /etc/nginx/nginx.conf
worker_processes auto;          # Use all available CPU cores
worker_rlimit_nofile 65535;     # Max open files per worker

events {
    worker_connections 4096;    # Max simultaneous connections per worker
    multi_accept on;            # Accept multiple connections at once
    use epoll;                  # Best I/O method for Linux
}

HTTP Block Optimizations

http {
    # Basic performance
    sendfile           on;
    tcp_nopush         on;
    tcp_nodelay        on;
    keepalive_timeout  65;
    keepalive_requests 1000;
    server_tokens      off;

    # File descriptor cache
    open_file_cache max=10000 inactive=30s;
    open_file_cache_valid    60s;
    open_file_cache_min_uses 2;
    open_file_cache_errors   on;

    # Buffer tuning
    client_body_buffer_size     128k;
    client_max_body_size        50m;
    client_header_buffer_size   1k;
    large_client_header_buffers 4 16k;

    # Gzip compression
    gzip              on;
    gzip_vary         on;
    gzip_proxied      any;
    gzip_comp_level   5;
    gzip_buffers      16 8k;
    gzip_http_version 1.1;
    gzip_min_length   256;
    gzip_types
        application/atom+xml application/javascript application/json
        application/rss+xml application/vnd.ms-fontobject
        application/x-font-ttf application/x-web-app-manifest+json
        application/xhtml+xml application/xml font/opentype
        image/svg+xml image/x-icon text/css text/plain text/x-component;
}

FastCGI Page Cache (for WordPress and PHP sites)

FastCGI caching stores fully rendered PHP pages at the Nginx level — bypassing PHP and MySQL entirely for cached requests. This is the single most impactful optimization for WordPress sites.

# In nginx.conf http block — define the cache zone
fastcgi_cache_path /var/cache/nginx/fastcgi
    levels=1:2
    keys_zone=PHPCACHE:100m
    inactive=60m
    max_size=1g;
# In your server block
fastcgi_cache_key "$scheme$request_method$host$request_uri";

# Don't cache for logged-in users or POST requests
set $no_cache 0;
if ($request_method = POST)         { set $no_cache 1; }
if ($query_string != "")            { set $no_cache 1; }
if ($http_cookie ~* "wordpress_logged_in") { set $no_cache 1; }

location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;

    fastcgi_cache PHPCACHE;
    fastcgi_cache_valid 200 60m;
    fastcgi_cache_bypass $no_cache;
    fastcgi_no_cache    $no_cache;
    add_header X-FastCGI-Cache $upstream_cache_status;
}
# Create the cache directory
sudo mkdir -p /var/cache/nginx/fastcgi
sudo chown www-data:www-data /var/cache/nginx/fastcgi
sudo nginx -t && sudo systemctl reload nginx

After enabling, check cache hits with: curl -I https://yourdomain.com | grep X-FastCGI-Cache — you should see HIT on repeat requests.


Part 2: Redis Object Caching

Redis stores frequently accessed data in memory — eliminating redundant database queries. For WordPress, it reduces database load by 60–90% on active sites. For web applications, it’s equally transformative for session storage and query caching.

Install Redis

sudo apt install redis-server -y
sudo systemctl enable redis-server
sudo systemctl start redis-server

Tune Redis for a VPS (low memory usage)

sudo nano /etc/redis/redis.conf
# Bind to localhost only (don't expose Redis externally)
bind 127.0.0.1

# Set max memory (adjust to ~20% of your VPS RAM)
maxmemory 512mb

# Eviction policy — remove least recently used keys when full
maxmemory-policy allkeys-lru

# Disable persistence if you only need a cache (faster, no disk writes)
save ""
appendonly no
sudo systemctl restart redis-server

# Test Redis is working
redis-cli ping  # Should return: PONG

WordPress Redis Integration

Install the Redis Object Cache plugin in WordPress, then add to wp-config.php:

define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
define('WP_CACHE', true);

Activate the plugin and enable object cache. Database query counts on a typical WordPress page drop from 30–50 queries to 5–10 after Redis is active.


Part 3: PHP-FPM Tuning

PHP-FPM’s default pool settings are far too conservative for a VPS. These settings govern how many PHP workers are available — too few and requests queue up; too many and you exhaust RAM.

sudo nano /etc/php/8.3/fpm/pool.d/www.conf
; Process management — dynamic is best for most VPS setups
pm = dynamic

; Maximum number of PHP workers
; Formula: (Total RAM - OS/DB overhead) / PHP worker memory usage
; Example: (4096MB - 1024MB overhead) / 50MB per worker = ~60 max
pm.max_children = 50

; Workers to start on launch
pm.start_servers = 10

; Minimum idle workers to keep available
pm.min_spare_servers = 5

; Maximum idle workers (prevents too many idle processes)
pm.max_spare_servers = 20

; Recycle workers after this many requests (prevents memory leaks)
pm.max_requests = 500

; Slow log — log PHP processes slower than 5 seconds
slowlog = /var/log/php-fpm-slow.log
request_slowlog_timeout = 5s
sudo systemctl restart php8.3-fpm

PHP.ini Performance Settings

sudo nano /etc/php/8.3/fpm/php.ini
; Memory
memory_limit = 256M

; OPcache — caches compiled PHP bytecode in memory (huge performance win)
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.save_comments=1
opcache.fast_shutdown=1

; Execution limits
max_execution_time = 60
max_input_time = 60

; Upload limits
upload_max_filesize = 64M
post_max_size = 64M
sudo systemctl restart php8.3-fpm

OPcache alone typically delivers a 30–50% reduction in PHP response times by eliminating repeated parsing and compilation of PHP files.


Part 4: MySQL / MariaDB Tuning

Database performance is often the primary bottleneck for web applications. These settings tune MariaDB/MySQL for a 4 GB RAM VPS:

sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf
[mysqld]
# InnoDB buffer pool — cache frequently accessed data and indexes in RAM
# Set to ~50-70% of available RAM after OS and app overhead
innodb_buffer_pool_size = 1G

# Multiple buffer pool instances for better concurrency
innodb_buffer_pool_instances = 2

# Log file size — larger = fewer flushes = better performance
innodb_log_file_size = 256M

# Flush method — O_DIRECT avoids double buffering with OS cache
innodb_flush_method = O_DIRECT

# Reduce fsync calls (slightly less durable but significantly faster)
innodb_flush_log_at_trx_commit = 2

# Query cache (MariaDB only — removed in MySQL 8.0)
query_cache_type = 1
query_cache_size = 64M
query_cache_limit = 2M

# Connection settings
max_connections = 150
thread_cache_size = 16
table_open_cache = 4000

# Temporary table sizes (reduce disk temp tables)
tmp_table_size = 64M
max_heap_table_size = 64M
sudo systemctl restart mariadb

# Verify buffer pool is active
mysql -e "SHOW ENGINE INNODB STATUS\G" | grep "Buffer pool size"

Part 5: Swap Configuration

Swap acts as overflow memory — when RAM is exhausted, the OS moves inactive data to disk. Without swap, your server will kill processes (OOM killer) when it runs out of RAM. With too much swap, it uses disk constantly and the server becomes sluggish.

Check current swap

free -h
swapon --show

Create a swap file (if none exists)

# Create a 2 GB swap file (recommended: 1-2x RAM for VPS under 8 GB)
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# Make it permanent across reboots
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# Verify
free -h

Tune Swappiness

vm.swappiness controls how aggressively the kernel moves data from RAM to swap. The default (60) is too aggressive for a VPS — it swaps too early, slowing performance unnecessarily.

# Temporarily
sudo sysctl vm.swappiness=10

# Permanently
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

A swappiness of 10 means the kernel only starts swapping when RAM usage reaches 90% — keeping applications in fast RAM as long as possible.


Part 6: Kernel TCP Tuning with BBR

BBR (Bottleneck Bandwidth and Round-trip propagation time) is Google’s TCP congestion control algorithm. It significantly improves throughput on high-latency or lossy connections — exactly the conditions users experience when connecting to your VPS from across the country or around the world.

Enable BBR

# Check current congestion control algorithm
sysctl net.ipv4.tcp_congestion_control

# Enable BBR
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

# Verify BBR is active
sysctl net.ipv4.tcp_congestion_control
# Should output: net.ipv4.tcp_congestion_control = bbr

Additional Network Buffer Tuning

sudo nano /etc/sysctl.conf

Add these lines:

# Increase network buffer sizes for high-throughput connections
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.rmem_default = 31457280
net.core.wmem_default = 31457280
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728

# Increase the connection backlog queue
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# Reuse closed TIME_WAIT connections faster
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

# Increase local port range for outbound connections
net.ipv4.ip_local_port_range = 1024 65535
sudo sysctl -p

Part 7: File System and I/O Tuning

Use the deadline or mq-deadline I/O scheduler

# Check current scheduler
cat /sys/block/sda/queue/scheduler

# Set mq-deadline for SSDs (best for VPS with SSD storage)
echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler

# Make it permanent
sudo nano /etc/udev/rules.d/60-scheduler.rules
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"

Increase inotify limits (for applications watching many files)

echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Measuring the Impact

After applying all optimizations, re-run your baseline benchmarks:

# Web server benchmark — compare before and after
ab -n 10000 -c 100 http://localhost/

# Check cache hit rate
curl -I https://yourdomain.com | grep X-FastCGI-Cache

# Database performance
mysqlslap --user=root --password --auto-generate-sql --concurrency=50 --iterations=10

# Memory usage after tuning
free -h

# Confirm BBR is active and network buffers are set
sysctl net.ipv4.tcp_congestion_control
sysctl net.core.rmem_max

Performance Optimization Summary

Optimization Typical Improvement Effort
Nginx FastCGI cache 5–50x faster PHP page delivery Medium
Redis object cache 60–90% fewer DB queries Low
PHP OPcache 30–50% faster PHP execution Low
PHP-FPM pool tuning Eliminates request queuing Low
MariaDB buffer pool 2–10x faster DB queries Low
Gzip compression 50–70% smaller transfers Low
BBR TCP 20–40% better throughput (high latency) Very low
Swap + swappiness tuning Prevents OOM crashes Very low
Nginx worker tuning Higher concurrent connection handling Low

Final Thoughts

The optimizations in this guide are not theoretical improvements — they’re the configurations used by production web infrastructure serving millions of requests daily. Nginx FastCGI caching, Redis, PHP OPcache, BBR TCP, and a properly tuned MariaDB buffer pool together transform a standard VPS into a high-performance server capable of handling serious traffic.

Start with the highest-impact changes first: FastCGI caching, Redis, OPcache, and the InnoDB buffer pool. These four changes alone typically deliver 5–10x performance improvements on WordPress and PHP sites. Then layer in the kernel tuning and swap configuration for maximum stability and throughput.

Remember: the best hardware upgrade is often a configuration upgrade. A well-tuned VPS.DO $20/month plan consistently outperforms a poorly configured $100/month plan. Apply these settings, measure the results, and you’ll see the difference.

Questions about your specific performance bottleneck? VPS.DO’s support team is available 24/7 →


Related articles you might find useful:

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!