Speed is one of the most important factors in hosting websites and web apps. With a fast server, you will have happier users, better SEO, and a smoother experience, and if you are running PHP sites, two tools make all the difference, including Nginx and PHP-FPM. This guide will teach you how to optimize Nginx and PHP-FPM on VPS for a faster experience.
If you are looking for optimized performance, our flexible and high-performance VPS hosting solutions provide fully managed environments with pre-tuned configurations, ensuring your PHP applications run ultra-fast from day one.
Requirements To Optimize Nginx and PHP-FPM on VPS
This guide provides a complete process to install a high-performance web server stack on an Ubuntu VPS.
You must access your server as a non-root user with sudo privileges and install the required packages, Nginx mainline, and PHP 8.3 from the Ondřej Surý PPA.
Run the system update and install the required packages with the following command:
sudo apt updatesudo apt install software-properties-common curl ca-certificates gnupg lsb-release -y
Add the Nginx and PHP PPA repository with the following commands:
sudo add-apt-repository -y ppa:ondrej/nginxsudo add-apt-repository -y ppa:ondrej/php
Run the system update again to apply the changes:
Then, install Nginx and PHP-FPM with its common extensions with the following command:
sudo apt install nginx php8.3-fpm php8.3-cli php8.3-mbstring php8.3-xml \php8.3-curl php8.3-zip php8.3-gd php8.3-intl php8.3-mysql php8.3-opcache -y
Note: If you prefer to use another PHP version, like PHP 8.2, you can replace it.
Also, install the following useful tools:
sudo apt install htop iotop net-tools unzip git wrk -y
Establish a Performance Baseline for Nginx and PHP
It is recommended to establish a known performance baseline before implementing any performance optimizations.
Verify your Nginx and PHP-FPM service status with the following commands:
sudo systemctl status nginx php8.3-fpm --no-pagernginx -vphp -v
Create a quick and simple PHP test file with the commands below and set the correct ownership for it:
sudo mkdir -p /var/www/example/publicecho "<?php echo 'OK';" | sudo tee /var/www/example/public/index.phpsudo chown -R www-data:www-data /var/www/example
Now use the command below to configure a minimal Nginx server block:
sudo tee /etc/nginx/sites-available/example.conf >/dev/null <<'NGINX'server { listen 80 default_server reuseport; server_name _; root /var/www/example/public; index index.php; access_log /var/log/nginx/example.access.log; error_log /var/log/nginx/example.error.log warn; location / { try_files $uri /index.php?$args; } location ~ \.php$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_read_timeout 30s; }}NGINX
Note: The server_name _; means Nginx will respond to any hostname. If you’re just testing or using an IP address, you can leave it as an underscore.
Remove or disable the old default site with the command below:
sudo rm /etc/nginx/sites-enabled/default
Enable the new configuration and check for syntax errors with the commands below:
sudo ln -sf /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/example.confsudo nginx -t
Reload Nginx to apply the changes:
sudo systemctl reload nginx
You can use the following command to run a quick benchmark:
sudo wrk -t2 -c50 -d30s http://127.0.0.1/
Record the requests and latency; we will compare them after tuning.
System-Level VPS Optimization
In this step, you can perform system tuning that is safe for most VPSs. You can adjust the number of files and network connections the system can handle, and the kernel's TCP networking stack.
This prevents your server from becoming a bottleneck under load.
Tuning File Descriptors and Connection Limits
The default limit on open files and connections (file descriptors) on Linux is often too low for a busy web server. Nginx uses a file descriptor for each client connection, and if this limit is reached, the server will start refusing requests.
You can safely raise this global limit with the following command:
echo '* soft nofile 1048576* hard nofile 1048576root soft nofile 1048576root hard nofile 1048576' | sudo tee /etc/security/limits.d/99-nofile.conf
Then, create the system configuration directory and set the system-wide file descriptor limit for services with the commands below:
sudo mkdir -p /etc/systemd/system.conf.d echo '[Manager]DefaultLimitNOFILE=1048576' | sudo tee /etc/systemd/system.conf.d/99-nofile.conf
Reload the system configuration to apply the changes:
sudo systemctl daemon-reexec
Optimizing Kernel TCP and Networking Parameters
The Linux kernel's default network settings are designed for general-purpose use, not high-performance web serving. You can optimize it for a higher number of concurrent connections by increasing connection backlogs, enabling socket reuse to reduce TIME-WAIT states, and tuning TCP buffer sizes for better throughput.
The settings we use are kept moderate intentionally, as they work well on most VPS providers and still provide a clear performance boost.
sudo tee /etc/sysctl.d/99-tuned.conf >/dev/null <<'SYS'net.core.somaxconn = 65535net.core.netdev_max_backlog = 16384 net.ipv4.tcp_max_syn_backlog = 8192net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_fin_timeout = 15net.ipv4.tcp_tw_reuse = 1 net.ipv4.ip_local_port_range = 10240 65535 net.ipv4.tcp_keepalive_time = 120net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 5 net.ipv4.tcp_rmem = 4096 131072 8388608net.ipv4.tcp_wmem = 4096 131072 8388608SYS
Reload the system settings to apply the changes:
PHP-FPM Performance Tuning
While system tuning helps your server run faster overall, most of the real performance boost happens when you optimize PHP-FPM. In this step, we’ll look at two key things:
- Speeding up PHP execution with OPcache and JIT.
- Adjusting the PHP-FPM process manager so it can handle your site’s traffic efficiently without wasting memory or CPU.
Setting these up correctly can make PHP run 2 to 5 times faster and keep your server’s performance much more stable.
PHP-FPM OPcache and JIT Configuration
In this step, you can set up OPcache to use memory efficiently and keep cached scripts longer, and also turn on the Just-In-Time (JIT) compiler for an extra speed boost by converting PHP code directly into machine code.
Open the PHP-FPM Ini file with the following command:
sudo nano /etc/php/8.3/fpm/php.ini
Add the following OPcache and JIT settings at the end of the file:
; ---- OPCACHE ----opcache.enable=1opcache.enable_cli=0opcache.memory_consumption=256opcache.interned_strings_buffer=32opcache.max_accelerated_files=65000opcache.validate_timestamps=0opcache.revalidate_freq=0opcache.jit=1255opcache.jit_buffer_size=64M ; ---- realpath ----realpath_cache_size=4096krealpath_cache_ttl=600
Also, find the lines below and change their values to:
expose_php=0memory_limit=256M
PHP-FPM Process Manager Configuration
PHP-FPM’s process manager (pm) controls how PHP handles multiple requests at the same time. Picking the wrong mode can waste server resources or even cause crashes when traffic gets heavy.
In this step, we will explain three main process management methods, including on-demand, static, and dynamic. Additionally, you will learn an easy method to calculate the optimal number of PHP worker processes based on your server's memory.
Choose Process Manager Models
Open the PHP-FPM pool configuration file with the command below:
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
Choose one of the following process manager models and edit the file:
Option A: On-demand, which is good for sites with spiky traffic:
pm = ondemandpm.max_children = (RAM_for_PHP / avg_php_rss)pm.process_idle_timeout = 10spm.max_requests = 1000
Option B: Static with predictable RAM and max throughput:
pm = staticpm.max_children = Npm.max_requests = 2000
Option C: Dynamic (Balanced approach):
pm = dynamicpm.max_children = Npm.start_servers = min(4, N/4)pm.min_spare_servers = min(4, N/4)pm.max_spare_servers = max(8, N/2)pm.max_requests = 2000
How to Calculate Number of PHP Workers (N)?
1. Measure the average memory used by one PHP worker:
Install smem to measure memory:
sudo apt install smem -yps -o rss= -C php-fpm8.3 | awk '{sum+=$1} END {print "avg_kb=" (sum/NR)}'
For example, if the output shows avg_kb=60000, it means ~60 MB per PHP worker.
2. Decide how much RAM to give PHP:
For example, on an 8 GB VPS running only Nginx with PHP, reserve ~2.5 GB for OS, ~0.5 GB for Nginx, and 5 GB RAM for PHP.
3. Calculate N with:
N = floor(RAM_for_PHP_MB / avg_worker_MB)
For example, 5120 MB ÷ 60 MB ≈ 85 workers.
Start lower and increase only if the CPU is not maxed and the RAM is safe.
Other recommended settings in www.conf, you can adjust, include:
; Use the socket, not TCP, for lower latencylisten = /run/php/php8.3-fpm.socklisten.owner = www-datalisten.group = www-datalisten.mode = 0660 ; Slowlog to catch slow scriptsrequest_slowlog_timeout = 2sslowlog = /var/log/php8.3-fpm/slow.log ; Status page (enable for monitoring)pm.status_path = /fpm-status
Once you are done, apply the changes by reloading PHP-FPM:
sudo systemctl reload php8.3-fpm
Nginx Global Performance Tuning
At this point, you can configure Nginx at a global level to handle high concurrency, implement smart caching strategies, and reduce latency through TCP optimizations and compression.
Open the Nginx configuration file with the command below:
sudo nano /etc/nginx/nginx.conf
Edit the file as shown below:
user www-data;worker_processes auto;worker_rlimit_nofile 1048576; events { worker_connections 65535; multi_accept on; use epoll;} http { include mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; aio on; server_tokens off; keepalive_timeout 20s; keepalive_requests 1000; gzip on; gzip_comp_level 5; gzip_min_length 1024; gzip_proxied any; gzip_types text/plain text/css application/json application/javascript application/xml \ application/rss+xml text/javascript image/svg+xml; client_max_body_size 32m; client_body_timeout 20s; send_timeout 20s; open_file_cache max=20000 inactive=60s; open_file_cache_valid 120s; open_file_cache_min_uses 2; open_file_cache_errors on; fastcgi_buffers 16 32k; fastcgi_buffer_size 64k; fastcgi_connect_timeout 3s; fastcgi_send_timeout 30s; fastcgi_read_timeout 30s; fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=PHPFCGI:128m inactive=60m max_size=4g; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*;}
FastCGI Microcaching High-Impact Performance Gain
For dynamic PHP applications with heavy traffic, FastCGI microcaching provides the most performance improvements possible.
You can create a FastCGI cache configuration snippet that defines the smart caching rules:
sudo tee /etc/nginx/snippets/fastcgi-php.conf >/dev/null <<'NGINX'include fastcgi_params;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;fastcgi_pass unix:/run/php/php8.3-fpm.sock; 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_|comment_author|PHPSESSID|laravel_session") { set $no_cache 1; } fastcgi_cache_bypass $no_cache;fastcgi_no_cache $no_cache; fastcgi_cache PHPFCGI;fastcgi_cache_key "$scheme$request_method$host$request_uri";fastcgi_cache_valid 200 301 302 10s;fastcgi_cache_valid 404 1s;add_header X-Cache $upstream_cache_status always; fastcgi_buffers 16 32k;fastcgi_buffer_size 64k; fastcgi_intercept_errors on;NGINX
Then, integrate the cache into the Nginx server blocks by editing the /etc/nginx/sites-available/example.conf file:
server { listen 80 default_server reuseport; server_name _; root /var/www/example/public; index index.php; access_log /var/log/nginx/example.access.log; error_log /var/log/nginx/example.error.log warn; location / { try_files $uri /index.php?$args; } location ~ \.php$ { include /etc/nginx/snippets/fastcgi-php.conf; } location ^~ /admin { set $no_cache 1; try_files $uri /index.php?$args; location ~ \.php$ { include /etc/nginx/snippets/fastcgi-php.conf; } }}
Once you are done, check for syntax errors and reload Nginx to apply the changes:
sudo nginx -tsudo systemctl reload nginx
Maximizing PHP Performance: Safe Deployment with OPcache
Since you have set opcache.validate_timestamps=0 to get the best performance, PHP won’t automatically notice new code changes. That means you need a reliable way to deploy updates so new code runs right away.
This process safely clears OPcache and reloads PHP-FPM without losing the speed benefits:
sudo -u www-data php -r 'opcache_reset();' 2>/dev/null || truesudo systemctl reload php8.3-fpmsudo systemctl reload nginx
Nginx Cache and PHP-FPM Health Monitoring
At this point, you must validate your tuning efforts, track cache effectiveness, and identify bottlenecks.
Use the following command to access the log that has an X-Cache header; check the cache hit rate:
grep 'X-Cache' /var/log/nginx/example.access.log | awk '{print $NF}' | sort | uniq -c
Then, enable the status in pool (pm.status_path=/fpm-status) and add the following settings:
location /fpm-status { allow 127.0.0.1; deny all; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; fastcgi_pass unix:/run/php/php8.3-fpm.sock;}
Use the command below to view the slow log:
tail -f /var/log/php8.3-fpm/slow.log
VPS Performance Validation and Benchmarking
At this step, you can use the same tools from the baseline test and measure your optimizations.
Warm cache with a quick run:
wrk -t2 -c50 -d15s http://127.0.0.1/
wrk -t4 -c200 -d60s http://127.0.0.1/
After applying the optimizations, you should notice:
- More requests per second, especially for cached GET requests
- Lower p95 latency on cached pages or endpoints
- CPU usage stays below 80% at your target request rate
- Memory usage stays within expected limits; you can check with htop or ps.
That's it, you are done with optimizing Nginx and PHP-FPM on VPS for a faster experience.
Conclusion
Optimizing PHP-FPM and Nginx on VPS provides faster page loads, higher throughput, and stable resource usage. You just need to remember that every VPS and app is unique, you must start with safe and measured defaults, benchmark before and after changes.
With PerLod Hosting, you will get the best-tuned server stack and robust managed hosting.
We hope you enjoy this guide. Subscribe to our X and Facebook channels to get the latest articles on VPS performance optimization.
For further reading:
Building your own 3D render farm
Improve VPS Performance with the Best Solutions
Set Up a Private VPN Server on Your VPS with WireGuard