← All posts

Tutorial Backend

How to Set Up PHP-FPM with Apache for Better Performance

Replace mod_php with PHP-FPM on Apache to boost performance, isolate processes, and serve more requests per second.

Difficulty Intermediate Time 30 min
Apache and PHP-FPM running as separate process pools connected by a FastCGI socket for improved PHP performance.

By default most LAMP tutorials wire PHP into Apache via mod_php — it works, but it loads a PHP interpreter into every Apache worker process whether that worker is serving PHP or a static image. Switching to PHP-FPM (FastCGI Process Manager) hands PHP off to its own dedicated pool of processes, freeing Apache workers to do what they are good at and letting you tune PHP concurrency independently. By the end of this guide Apache will be passing every .php request to a PHP-FPM Unix socket, mod_php will be fully disabled, and your server will handle significantly more concurrent traffic on the same hardware.

Prerequisites

  • Ubuntu 22.04 LTS (steps are nearly identical on 20.04 and Debian 12)
  • Apache 2.4 already installed and running — run apache2 -v to confirm
  • PHP already installed — this guide uses PHP 8.2; adjust the version number throughout if yours differs
  • Root or passwordless sudo access
  • At least one Apache virtual host configured and serving a PHP file so you can verify the switch works

Step 1 — Install PHP-FPM

Install the FPM variant of your PHP version from the ondrej/php PPA (already on your system if you installed PHP from there) or from the default Ubuntu repos.

sudo apt update sudo apt install php8.2-fpm -y

Confirm the service started cleanly:

sudo systemctl status php8.2-fpm

You should see active (running). PHP-FPM creates a Unix socket at /run/php/php8.2-fpm.sock by default — you will point Apache at this socket in a later step.

Step 2 — Enable the Required Apache Modules

PHP-FPM communicates with Apache over FastCGI. You need two Apache modules: proxy_fcgi (the FastCGI proxy) and setenvif (used by the standard FPM configuration snippet). Enable them now:

sudo a2enmod proxy_fcgi setenvif

Note: mod_proxy is pulled in automatically as a dependency of proxy_fcgi. You do not need to enable it separately.

Step 3 — Disable mod_php

Running mod_php and PHP-FPM at the same time causes conflicts — Apache will try to handle .php files twice. Disable mod_php for PHP 8.2 (and any other PHP version you have loaded) before going further.

sudo a2dismod php8.2

If you previously had another PHP version loaded, disable that too. For example:

sudo a2dismod php8.1

Verify no php module remains enabled:

apache2ctl -M | grep php

The command should return nothing. If you still see a php8.x_module listed, run a2dismod for that version before continuing.

Step 4 — Enable the PHP-FPM Apache Configuration

When you installed php8.2-fpm, it dropped a ready-made Apache configuration snippet into /etc/apache2/conf-available/. Enable it with:

sudo a2enconf php8.2-fpm

Take a look at what the conf file contains so there are no surprises:

cat /etc/apache2/conf-available/php8.2-fpm.conf

You will see something like the following. The FilesMatch block matches every .php request and the SetHandler directive routes it to the FPM Unix socket:

FilesMatch ".php$" SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost" /FilesMatch

No further edits are needed for a standard single-site setup.

Step 5 — Restart Apache and PHP-FPM

Reload both services to apply every change made so far:

sudo systemctl restart php8.2-fpm sudo systemctl restart apache2

Check that Apache came back up cleanly:

sudo systemctl status apache2

Gotcha: If Apache fails to start, run sudo apache2ctl configtest to pinpoint the bad line before you dig through logs. A common culprit is a virtual host still referencing a mod_php directive such as php_value or php_flag — those directives only work with mod_php and must be moved into a php.ini or pool www.conf override when using FPM.

Step 6 — Verify PHP Is Running Through FPM

Create a temporary info file in your web root to confirm which PHP SAPI is active. Replace /var/www/html with your actual document root if it differs.

echo "php phpinfo();" | sudo tee /var/www/html/info.php

Open http://your-server-ip/info.php</code> in a browser and search the page for the Server API row. It must read FPM/FastCGI — not Apache 2.0 Handler, which would mean mod_php is still active.

Once confirmed, delete the file immediately — leaving a public phpinfo page on a production server is a security risk:

sudo rm /var/www/html/info.php

Step 7 — Tune the PHP-FPM Pool for Your Server

The default FPM pool configuration is conservative. Open the pool file for editing:

sudo nano /etc/php/8.2/fpm/pool.d/www.conf

The three most impactful settings for a typical VPS with 2 GB RAM are:

  • pm = dynamic — keeps a variable number of workers, scaling up under load and releasing memory when idle. Leave this as-is unless you have profiled a reason to use static or ondemand.
  • pm.max_children — the hard cap on concurrent PHP workers. A safe starting formula: available RAM in MB divided by average PHP process size in MB. Check your average process size with the command below. For a 2 GB server running a typical WordPress or Laravel app, 20 to 30 is a reasonable starting point.
  • pm.max_requests — restart a worker after this many requests to prevent memory leaks from accumulating. Set it to 500 as a safe default.

Check your average PHP process size in MB:

ps --no-headers -o rss -C php-fpm8.2 | awk '{sum+=$1} END {print sum/NR/1024 " MB"}'

Then set the pool values accordingly:

pm = dynamic pm.max_children = 20 pm.start_servers = 4 pm.min_spare_servers = 2 pm.max_spare_servers = 6 pm.max_requests = 500

After editing, reload FPM to apply without dropping active connections:

sudo systemctl reload php8.2-fpm

Wrap-up

Apache is now fully decoupled from the PHP interpreter. Every .php request flows through a Unix socket to a dedicated PHP-FPM worker pool, while Apache workers stay lean and handle static assets without carrying PHP overhead. The practical gains — lower memory per Apache process, tunable PHP concurrency, and per-pool user isolation — make PHP-FPM with Apache the right default for any production LAMP server. From here, the natural next steps are enabling OPcache inside /etc/php/8.2/fpm/php.ini for bytecode caching, and setting up a separate FPM pool per virtual host if you are running multiple applications on the same box.