Debug School

rakesh kumar
rakesh kumar

Posted on

PHP-FPM Pools Explained: When to Use Them, How They Override php.ini, and How to Secure Laravel Services

Meta description: Learn PHP-FPM pools in simple words, including php.ini overrides, worker limits, Unix sockets, security scenarios, and a step-by-step setup guide.

Suggested slug: php-fpm-pools-security-php-ini-laravel-guide

A PHP-FPM pool is a group of PHP worker processes with its own configuration. Apache or Nginx forwards PHP requests to the pool, and an available worker executes the application.

Think of a shared office: PHP-FPM manages the staff, while each pool is a department with its own staff limit and rules. Booking, payment, and file services can use separate departments.

This guide uses Ubuntu-style paths and an Apache example. Adapt paths, accounts, PHP versions, and limits to your server. The configurations and outputs are illustrative, not evidence that your production server has been tested.

1. What Does Each Component Do?

Component Simple purpose Example
Apache or Nginx Receives HTTP requests and serves static content Visitor opens a booking page
PHP-FPM service Starts and supervises PHP workers php8.4-fpm
FPM pool Groups workers with service-specific rules booking
Worker Executes PHP application code Laravel controller and database query
Unix socket Local communication endpoint /run/php/booking.sock
php.ini Provides general PHP configuration Default upload limit
Pool configuration Defines worker behavior and PHP overrides Booking pool settings

Request example: a visitor opens /booking, Apache routes the PHP request to the booking socket, a worker executes Laravel, and Apache returns the response. A socket filename is only a name; it does not prove which PHP version owns it.

2. When and Why Should You Use Separate Pools?

Situation Why a separate pool helps
Multiple Laravel services share a host Configure workers and settings separately
File uploads need different limits Give the file service its own upload policy
Services need different Linux permissions Use a separate worker account per service
You need clearer troubleshooting Attribute worker activity and logs to a pool
You are migrating PHP versions gradually Run separate versioned FPM services and switch one application's routing

A small single application may need just one pool. More pools do not automatically mean more speed or security. Each additional worker competes for the same CPU, memory, database connections, and disk resources.

A pool belongs to its running FPM instance. A PHP 8.2 pool cannot turn itself into PHP 8.4 merely by changing a filename.

3. How Does a Pool Override php.ini?

General defaults:

; /etc/php/8.4/fpm/php.ini
upload_max_filesize = 2M
Enter fullscreen mode Exit fullscreen mode

Pool override:

; /etc/php/8.4/fpm/pool.d/booking.conf
php_admin_value[upload_max_filesize] = 10M
Enter fullscreen mode Exit fullscreen mode

Expected effective value for booking:

upload_max_filesize = 10M
Enter fullscreen mode Exit fullscreen mode
Pool General default Pool override Effective PHP value
Booking 2M 10M 10M
File service 2M 50M 50M
Payment 2M None 2M, assuming no other applicable override

Changing the default to 5M leaves booking at 10M. A pool without an override inherits the new default after configuration is applied.

php_admin_value and php_admin_flag set administrative options that application code cannot override with ini_set(). Ordinary php_value settings do not provide the same administrative lock; whether another layer can change an option also depends on that directive's changeability. This example simplifies the hierarchy: additional loaded INI files and other applicable configuration can also matter.

4. Does PHP-FPM Stop Hackers?

It provides useful controls, but it is not a replacement for patched software or secure application code.

Scenario What could happen Helpful control Remaining limitation
Repeated expensive requests All workers become occupied Worker limits, traffic limits, queueing The targeted service can still become unavailable
One service is compromised Attacker tries to access another application's secrets Different users and restrictive file permissions Shared groups or readable secrets can defeat separation
Oversized uploads Disk, bandwidth, or parser resources are consumed Proxy limits, PHP limits, Laravel validation Upload bytes do not predict extraction memory
Runtime vulnerability Exploitable PHP code path is triggered Patch the actual runtime and recycle workers Pool settings do not repair a runtime bug
Technical error disclosure Paths and stack details reach visitors Hide public errors and keep protected logs Laravel's own debug configuration also matters
Public FastCGI listener Untrusted clients reach PHP directly Permission-restricted local socket Socket permissions must match the deployment

Do not expose an FPM listener directly to the internet. Also avoid allowing upload directories to execute PHP. Different pool names with the same broadly privileged Linux account provide much less protection than genuinely restricted accounts.

Pools in one FPM instance share a master and may share facilities such as OPcache; they are not strong isolation boundaries for mutually untrusted tenants. Use separate instances, containers, or hosts when stronger separation is needed.

5. Step-by-Step Example Setup

Step 1: Inventory the Real Web Runtime

Read-only examples:

php -v
php-fpm8.4 -v
systemctl status php8.4-fpm --no-pager
Enter fullscreen mode Exit fullscreen mode

CLI output identifies CLI PHP only. Inspect Apache handlers and FPM pool socket definitions to map each site to its actual service. Existing servers may use different binary names or installation paths.

Step 2: Back Up Configuration and Record a Baseline

Before editing, preserve the affected FPM and Apache configuration and record existing functionality and errors. Use a unique backup directory rather than overwriting a previous backup.

Record login, uploads, booking behavior, response codes, and application logs. HTTP 200 alone does not establish that a workflow works.

Step 3: Prepare Accounts and Filesystem Access

Use an existing verified application account or provision a dedicated account using your deployment process. Give the worker read access to application code and write access only to necessary runtime directories.

For Laravel these commonly include storage and bootstrap/cache. Keep source code and secrets protected; do not make everything writable or use chmod -R 777.

The example below assumes bookingapp exists and Apache workers use www-data. LAMPP commonly uses a different account, such as daemon; confirm it first. Socket ownership and directory traversal permissions must permit the actual Apache account to connect.

Step 4: Define the Pool

Example file: /etc/php/8.4/fpm/pool.d/booking.conf

[booking]
user = bookingapp
group = bookingapp

listen = /run/php/booking.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = ondemand
pm.max_children = 3
pm.process_idle_timeout = 10s
pm.max_requests = 500

request_terminate_timeout = 60s
security.limit_extensions = .php
clear_env = yes

php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 10M
php_admin_value[post_max_size] = 12M
php_admin_flag[display_errors] = off
php_admin_flag[log_errors] = on
Enter fullscreen mode Exit fullscreen mode
Setting Meaning
user / group Identity executing PHP
listen Endpoint receiving FastCGI requests
pm.max_children Maximum workers for this pool
pm.max_requests Recycle a worker after a request count; not a leak fix
request_terminate_timeout Terminate a worker serving an overlong request
memory_limit PHP-managed memory ceiling, not a total pool/host cap

clear_env = yes can affect applications that depend on inherited environment variables. Explicitly provide necessary values through your established secret/configuration mechanism. Test logging destinations and session paths too.

Do not use this memory value as mitigation for an unpatched memory-corruption vulnerability. Apply the appropriate runtime fix first.

Step 5: Route Apache to the Socket

In the application's appropriate virtual-host configuration:

DocumentRoot /srv/booking/public

<Directory /srv/booking/public>
    Require all granted
    AllowOverride All

    <FilesMatch "\.php$">
        SetHandler "proxy:unix:/run/php/booking.sock|fcgi://localhost/"
    </FilesMatch>
</Directory>
Enter fullscreen mode Exit fullscreen mode

This fragment assumes the application exists at that path, required proxy modules are loaded, and Laravel rewrite rules are configured. Retain the existing TLS and virtual-host settings. Prevent execution in writable upload locations and inspect inherited PHP handlers before changing routing.

AllowOverride All supports an existing .htaccess deployment here; a carefully managed central configuration can restrict overrides further. Do not paste a new DocumentRoot over an unrelated existing site.

Step 6: Validate Before Applying

Ubuntu system-Apache example:

sudo php-fpm8.4 -t
sudo apachectl -t
Enter fullscreen mode Exit fullscreen mode

For LAMPP Apache use its own binary instead:

sudo /opt/lampp/bin/apachectl -t
Enter fullscreen mode Exit fullscreen mode

Expected messages include a successful FPM configuration test and:

Syntax OK
Enter fullscreen mode Exit fullscreen mode

If validation fails, correct or restore the configuration before reloading. Syntax checks do not verify all application behavior.

Step 7: Apply With a Planned Reload

After successful validation and an approved change window:

sudo systemctl reload php8.4-fpm
Enter fullscreen mode Exit fullscreen mode

Then reload the Apache instance actually serving the site. For LAMPP:

sudo /opt/lampp/bin/apachectl -k graceful
Enter fullscreen mode Exit fullscreen mode

These examples depend on service support and deployment details. Do not promise zero downtime: long requests, shutdown settings, connection handling, and failures can affect reload behavior. Reloading one shared FPM service can involve multiple pools.

Step 8: Verify From a Protected Web Request

Use an authenticated/internal diagnostic mechanism to inspect:

[
    'version' => PHP_VERSION,
    'sapi' => PHP_SAPI,
    'memory_limit' => ini_get('memory_limit'),
    'upload_max_filesize' => ini_get('upload_max_filesize'),
    'post_max_size' => ini_get('post_max_size'),
]
Enter fullscreen mode Exit fullscreen mode

Illustrative result:

{
  "version": "8.4.x",
  "sapi": "fpm-fcgi",
  "memory_limit": "256M",
  "upload_max_filesize": "10M",
  "post_max_size": "12M"
}
Enter fullscreen mode Exit fullscreen mode

Do not leave a public phpinfo or unrestricted diagnostics page online. Remove temporary diagnostics after verification. CLI ini_get() does not prove FPM settings.

6. Plan Worker Counts Against the Whole Server

Suppose you measure approximately 100 MiB of resident memory per busy worker and allocate 600 MiB to a particular pool. Six workers is a rough starting estimate before allowing for overhead, peaks, and measurement limitations.

14 pools × 3 workers = up to 42 PHP workers
42 × illustrative 100 MiB = about 4,200 MiB
Enter fullscreen mode Exit fullscreen mode

Reserve resources for Apache, databases, Keycloak, queues, the operating system, and other applications. RSS can double-count shared pages; PSS and real load tests improve the estimate. A 256M PHP memory limit is not an exact worker RSS cap. More workers can increase database contention and make performance worse.

7. Understand All Upload Limits

Layer Example limit Purpose
Proxy / web server 12 MiB request Reject excessive bodies early
PHP post_max_size 12M Limit complete POST body
PHP upload_max_filesize 10M Limit one uploaded file
Laravel file validation max:5120 Reject files over 5,120 KiB at application level

Multipart overhead means the request limit should leave room beyond the file itself. Coordinate related settings. A large PHP upload setting does not override a smaller upstream limit.

8. Verification and Rollback Checklist

  • Confirm each site reaches its intended socket and running PHP version.
  • Test login, sessions, uploads, important CRUD, and relevant sandbox integrations.
  • Verify runtime directory and database permissions.
  • Monitor FPM saturation, memory, errors, response times, and database connections.
  • Remember CLI queues and cron jobs use their configured executables, not Apache's pool routing.
  • Keep the previous configuration and a tested recovery procedure.
  • Never recover service by silently exposing a known-vulnerable old runtime to the internet.

Official References

Colourful Architecture: Requests and Configuration Overrides

Upload the accompanying diagram to your blog media storage and replace the relative URL below with its published URL.

PHP-FPM pool architecture showing request routing, php.ini defaults, pool overrides, and shared server resources

flowchart TD
    U["Visitors"] --> A["Apache: route PHP requests"]
    A --> B["Booking pool: booking.sock"]
    A --> F["File pool: file.sock"]
    A --> P["Payment pool: payment.sock"]
    I["php.ini: upload default 2M"] -.-> B
    I -.-> F
    I -.-> P
    B --> BO["Pool admin override: 10M"]
    F --> FO["Pool admin override: 50M"]
    P --> PO["No override: inherits 2M"]
    BO --> R["Shared CPU, RAM and databases"]
    FO --> R
    PO --> R
    classDef blue fill:#dbeafe,stroke:#2563eb,color:#172554
    classDef orange fill:#ffedd5,stroke:#ea580c,color:#7c2d12
    classDef green fill:#dcfce7,stroke:#16a34a,color:#14532d
    classDef purple fill:#f3e8ff,stroke:#9333ea,color:#581c87
    class U,A blue
    class B,F,P orange
    class BO,FO,PO green
    class I,R purple

Top comments (0)