Configuring PHP-FPM with NGINX: Per-VirtualHost Error Logging Best Practices


8 views

When running PHP applications through NGINX with PHP-FPM, developers often face the limitation of having all PHP errors, warnings, and notices dumped into a single log file (/var/log/php-fpm/www-error.log by default). This becomes particularly problematic in multi-site environments where:

  • Debugging specific applications becomes tedious
  • Log rotation affects all sites simultaneously
  • Security audits require isolation of logs
  • Performance monitoring needs per-application granularity

The most straightforward method involves modifying PHP-FPM pool configurations. Here's how to implement per-pool logging:

[www]
; Existing pool configuration
listen = /var/run/php-fpm/php-fpm.sock
user = nginx
group = nginx

; Add these directives for separate logging
catch_workers_output = yes
php_admin_flag[log_errors] = on
php_admin_value[error_log] = /var/log/php-fpm/$pool-error.log

For virtual hosts, create separate pools:

[site1]
listen = /var/run/php-fpm/site1.sock
user = site1
group = site1
php_admin_value[error_log] = /var/log/php-fpm/site1-error.log

[site2]
listen = /var/run/php-fpm/site2.sock
user = site2
group = site2
php_admin_value[error_log] = /var/log/php-fpm/site2-error.log

Match these pools with your NGINX virtual hosts:

server {
    listen 80;
    server_name site1.example.com;
    
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/site1.sock;
        # Other fastcgi parameters...
    }
    
    error_log /var/log/nginx/site1-error.log;
    access_log /var/log/nginx/site1-access.log;
}

server {
    listen 80;
    server_name site2.example.com;
    
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/site2.sock;
        # Other fastcgi parameters...
    }
    
    error_log /var/log/nginx/site2-error.log;
    access_log /var/log/nginx/site2-access.log;
}

For environments with frequently changing virtual hosts, implement dynamic log paths using environment variables:

[pool_template]
listen = /var/run/php-fpm/%n.sock
user = %n
group = %n
php_admin_value[error_log] = /var/log/php-fpm/%n-error.log

In your NGINX configuration:

fastcgi_param PHP_ERROR_LOG /var/log/php-fpm/$host-error.log;

Ensure proper log rotation by adding configurations to /etc/logrotate.d/:

/var/log/php-fpm/*-error.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    sharedscripts
    postrotate
        /usr/bin/systemctl reload php-fpm > /dev/null 2>&1 || true
    endscript
}
  • Permission errors: Ensure the PHP-FPM user has write access to log directories
  • Missing logs: Verify catch_workers_output = yes is set
  • Performance impact: Monitor disk I/O when logging heavily trafficked sites
  • Log format consistency: Consider implementing JSON formatting for easier parsing

When running PHP-FPM with NGINX, one common frustration is that all PHP errors (notices, warnings, and fatal errors) get funneled into a single log file (/var/log/php-fpm/www-error.log by default on Fedora systems). This becomes particularly problematic in multi-site environments where you need to debug issues for specific virtual hosts.

In production environments where multiple websites are hosted on the same server, having separate error logs for each virtual host provides several advantages:

  • Easier debugging when issues are isolated to specific sites
  • Better security through log separation
  • More efficient log rotation management
  • Simpler compliance with auditing requirements

We'll implement a solution that:

  1. Creates separate PHP-FPM pools for each virtual host
  2. Configures individual error logs for each pool
  3. Maintains proper permissions for log files

1. Create Separate PHP-FPM Pools

Edit or create pool configuration files in /etc/php-fpm.d/:

; /etc/php-fpm.d/example.com.conf
[example.com]
user = nginx
group = nginx
listen = /run/php-fpm/example.com.sock
listen.owner = nginx
listen.group = nginx
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
php_admin_value[error_log] = /var/log/php-fpm/example.com-error.log
php_admin_flag[log_errors] = on
catch_workers_output = yes

2. Configure NGINX Virtual Host

Update your NGINX server block to use the specific pool:

server {
    server_name example.com;
    # ... other configs ...
    
    location ~ \.php$ {
        fastcgi_pass unix:/run/php-fpm/example.com.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

3. Set Up Log Rotation

Create a logrotate configuration:

/var/log/php-fpm/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 640 nginx nginx
    sharedscripts
    postrotate
        /bin/kill -SIGUSR1 cat /run/php-fpm/php-fpm.pid 2>/dev/null 2>/dev/null || true
    endscript
}

Custom Error Levels per Virtual Host

You can specify different error reporting levels for each pool:

php_admin_value[error_reporting] = E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED

Separate PHP Settings per Site

php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M
php_admin_value[max_execution_time] = 300
  • Ensure the nginx user has write permissions to the log directory
  • Verify SELinux contexts if running on Fedora/RHEL: chcon -R -t httpd_log_t /var/log/php-fpm/
  • Check pool status with: systemctl status php-fpm
  • Test PHP-FPM configuration: php-fpm -t