Optimize PHP-FPM and Nginx for Ultra-Fast VPS

Updated on Apr 16, 2026
Mila H
10 MINS READ
Table of Contents
Optimize Nginx and PHP-FPM on VPS

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:

Bash
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:

Bash
sudo add-apt-repository -y ppa:ondrej/nginxsudo add-apt-repository -y ppa:ondrej/php

Run the system update again to apply the changes:

Bash
sudo apt update

Then, install Nginx and PHP-FPM with its common extensions with the following command:

Bash
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:

Bash
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:

Bash
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:

Bash
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:

Bash
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:

Bash
sudo rm /etc/nginx/sites-enabled/default

Enable the new configuration and check for syntax errors with the commands below:

Bash
sudo ln -sf /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/example.confsudo nginx -t

Reload Nginx to apply the changes:

Bash
sudo systemctl reload nginx

You can use the following command to run a quick benchmark:

Bash
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:

Bash
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:

Bash
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:

Bash
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.

Bash
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 # Keepalive to detect dead peersnet.ipv4.tcp_keepalive_time = 120net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 5 # Reduce TIME-WAIT pressure without being too aggressivenet.ipv4.tcp_rmem = 4096 131072 8388608net.ipv4.tcp_wmem = 4096 131072 8388608SYS

Reload the system settings to apply the changes:

Bash
sudo sysctl --system

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:

Bash
sudo nano /etc/php/8.3/fpm/php.ini

Add the following OPcache and JIT settings at the end of the file:

Bash
; ---- 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:

Bash
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:

Bash
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:

Bash
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:

Bash
pm = staticpm.max_children = Npm.max_requests = 2000

Option C: Dynamic (Balanced approach):

Bash
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:

Bash
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:

Bash
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:

Bash
; 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:

Bash
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:

Bash
sudo nano /etc/nginx/nginx.conf

Edit the file as shown below:

Bash
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:

Bash
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:

Bash
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:

Bash
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:

Bash
# pull new code / rsync / composer install / build steps...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:

Bash
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:

Bash
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:

Bash
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:

Bash
wrk -t2 -c50 -d15s http://127.0.0.1/

Then measure with:

Bash
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

Dynamic: Best for steady workloads with variable traffic; automatically adjusts workers. Static: Fixed number of workers; ideal for high, predictable load. Ondemand: Excellent for low or spiky traffic, saving memory.

For Nginx, use stub_status or log headers like X-Cache to track hits/misses. For PHP-FPM, enable pm.status_path and slow logs to track worker utilization and slow scripts. Combine with tools like htop, iotop, or wrk for real-time metrics.

After deploying new PHP code, reset OPcache (opcache_reset()) and reload PHP-FPM. This ensures the new scripts are loaded without needing to enable timestamp checks in production.