How PHP 8.4 Deprecation Notices Ate 14GB of Disk Space on a WooCommerce Server

· 9 min read

The Symptom: Disk Usage Alert at 94%

A monitoring alert fired on a Friday evening — disk usage on a client's 40GB VPS had jumped from 52% to 94% in three days. The server ran four WooCommerce stores and a handful of brochure sites, all managed through CloudPanel. Nothing had changed on any of the sites. No new uploads, no database growth, no forgotten backups.

I SSH'd in and ran the usual diagnostic:

du -sh /var/log/* | sort -rh | head -10

The PHP-FPM error log was 14GB.

14G /var/log/php8.4-fpm.log

One log file, three days old, consuming a third of the entire disk. I opened the tail end expecting a loop or a segfault. Instead, I found millions of identical deprecation notices.

What Was in the Log

tail -50 /var/log/php8.4-fpm.log

Every line was a variation of:

[22-May-2026 10:14:03 UTC] PHP Deprecated: loco_admin_init(): Implicitly marking parameter $hook as nullable is deprecated, the explicit nullable type must be used instead in /var/www/site-a/htdocs/wp-content/plugins/loco-translate/src/admin/init.php on line 42

I counted the unique sources:

grep -oP 'in /var/www/\S+' /var/log/php8.4-fpm.log | sort | uniq -c | sort -rn | head -20

Seven plugins across the four WooCommerce sites were generating notices. The top offenders:

  • Loco Translate — 12 unique call sites, firing on every admin page load
  • WooCommerce PayPal Payments — deprecation warnings in DisplayManager.php and RefundProcessor.php
  • Action Scheduler (bundled with WooCommerce) — ActionScheduler_ActionFactory.php triggered on every scheduled action run
  • Easy WP SMTP — its bundled Action Scheduler copy had the same issue
  • One Click Accessibility — a single deprecated call, but it fired on every front-end page load

Across four sites averaging 800 combined page views per hour plus WP-Cron and Action Scheduler runs, each generating 15-40 deprecation notices per request, the log was growing at roughly 200MB per hour.

Why This Happened

The hosting provider had upgraded the server from PHP 8.3 to 8.4 as part of a scheduled maintenance window. The client approved it — PHP 8.4 had been out for over a year, and WordPress 6.7+ officially lists beta support for it.

The problem is a specific deprecation introduced in PHP 8.4: implicitly nullable parameter types. In PHP 8.3 and earlier, this was perfectly valid:

function process_payment(string $token = null) {
    // $token is implicitly nullable because of the null default
}

PHP 8.4 now emits a deprecation notice every time this pattern is encountered. The correct syntax going forward is:

function process_payment(?string $token = null) {
    // Explicitly nullable with the ? prefix
}

The code still works — nothing breaks functionally. But PHP writes a deprecation notice to the error log on every single invocation. For a plugin like Action Scheduler that runs dozens of scheduled tasks per minute, each with multiple affected method signatures, the log output is enormous.

WordPress core itself fixed most of these in version 6.7 (Trac #60786). But third-party plugins lag behind, and some — particularly those supporting PHP 7.4 backwards compatibility — deliberately avoid the ?string syntax because it was not available in PHP 7.0.

The Immediate Fix: Stop the Bleeding

First priority was stopping the log from growing. I did not want to disable logging entirely — that would hide real errors.

Step 1: Suppress Deprecation Notices at the PHP-FPM Level

I edited the PHP-FPM pool configuration for each site. On CloudPanel, these live in /etc/php/8.4/fpm/pool.d/:

; Existing line:
php_admin_value[error_reporting] = E_ALL

; Changed to:
php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED

Then restarted PHP-FPM:

sudo systemctl restart php8.4-fpm

This is a targeted fix. E_DEPRECATED and E_USER_DEPRECATED are the only levels suppressed — fatal errors, warnings, and notices still get logged. On a production server, deprecation notices are noise. They are useful during development and testing, not while serving live traffic.

Step 2: Truncate the Log

With the flood stopped, I reclaimed the disk space:

sudo truncate -s 0 /var/log/php8.4-fpm.log

I used truncate rather than rm and recreate — truncating preserves the file descriptor that PHP-FPM holds open. Deleting the file would have left PHP-FPM writing to an unlinked inode until the next restart, and the disk space would not actually be freed.

Step 3: Add Log Rotation

The server had logrotate configured for nginx and MySQL logs, but not for the PHP-FPM log. I created /etc/logrotate.d/php8.4-fpm:

/var/log/php8.4-fpm.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data adm
    postrotate
        [ ! -f /run/php/php8.4-fpm.pid ] || kill -USR1 $(cat /run/php/php8.4-fpm.pid)
    endscript
}

The USR1 signal tells PHP-FPM to reopen its log file after rotation. Without the postrotate hook, PHP-FPM would continue writing to the old (now rotated and compressed) file.

I verified it with a dry run:

sudo logrotate -d /etc/logrotate.d/php8.4-fpm

The Proper Fix: Audit and Update Plugins

Suppressing log noise is a bandage. The real fix is updating the plugins generating the deprecation notices — or replacing them if updates are not available.

Scanning for Affected Plugins

I used grep to identify every affected file across all sites on the server:

grep -rn 'function.*\(.*[a-zA-Z] \$.*= null\)' /var/www/*/htdocs/wp-content/plugins/ \
  --include="*.php" | grep -v '\?' | head -40

This finds function declarations where a typed parameter has = null as a default but no ? nullable prefix. It is a rough heuristic, not a proper AST parse, but it catches the majority of cases.

For a more thorough audit, I installed PHPCompatibility via Composer on the server and ran it against each site's plugins:

composer global require phpcompatibility/phpcompatibility-wp:"*"
phpcs --standard=PHPCompatibilityWP \
      --runtime-set testVersion 8.4 \
      --severity=1 \
      --extensions=php \
      -s \
      /var/www/site-a/htdocs/wp-content/plugins/

The -s flag shows the sniff name alongside each finding, making it easy to filter for PHPCompatibility.FunctionDeclarations.RemovedImplicitlyNullableParam specifically.

Updating What Could Be Updated

I ran updates via WP-CLI across all four sites:

for site in /var/www/*/htdocs; do
  echo "=== $(basename $(dirname $site)) ==="
  wp --path="$site" plugin update --all --dry-run
done

The --dry-run flag shows what would be updated without touching anything. After reviewing the output, I ran the actual updates:

  • WooCommerce had fixed the Action Scheduler deprecation in version 9.6.0
  • WooCommerce PayPal Payments had a patch in version 3.1.2
  • Easy WP SMTP had been updated two weeks earlier with the fix

Two plugins had no fix available:

  • Loco Translate — the author was maintaining PHP 7.4 compatibility and had not yet adopted nullable types. I checked their GitHub issues and found a ticket acknowledging the problem with no timeline for a fix.
  • One Click Accessibility — abandoned, last updated 18 months ago.

For Loco Translate, I left it as-is — the deprecation suppression in PHP-FPM handles the noise, and the plugin still works correctly on PHP 8.4. When a fixed version ships, I will update and re-enable deprecation logging on a staging copy to verify.

For One Click Accessibility, I replaced it with WP Accessibility, which is actively maintained and PHP 8.4 clean.

What I Now Do on Every PHP Upgrade

This incident changed my PHP upgrade checklist. I already had a process for batch-upgrading PHP versions across multiple sites, but it focused on functional breakage — white screens, fatal errors, broken features. Deprecation log floods are a different failure mode that does not show up in browser testing.

My updated process:

  1. Before the upgrade, run PHPCompatibility against all plugin directories on the server. Flag anything with implicit nullable parameters.
  2. Upgrade PHP on a staging pool first and monitor /var/log/php*-fpm.log size for 24 hours. If the log grows faster than 50MB/day, investigate before promoting to production.
  3. Set error_reporting to exclude E_DEPRECATED on all production PHP-FPM pools by default. Re-enable on staging for testing.
  4. Ensure logrotate covers PHP-FPM logs on every server. I have seen this missing on CloudPanel, RunCloud, and manual nginx+PHP setups. cPanel/WHM typically handles it, but verify.
  5. Set a disk usage alert at 80%, not 90%. The difference between "I have time to investigate" and "the server is about to crash" is smaller than you think on a 40GB VPS.

The Bigger Picture

PHP 8.4's implicit nullable deprecation is the most prolific log-flooding deprecation since create_function() was deprecated in PHP 7.2. The difference is that create_function() was rare in modern code — almost nobody used it by 2018. Implicit nullable parameters are everywhere. WordPress core had dozens of instances. WooCommerce had hundreds. The long tail of plugins still catching up will generate noise for years.

The practical risk is not that the code breaks — it will not, until PHP 9.0 removes the pattern entirely. The risk is exactly what happened here: unmonitored logs filling disks, triggering cascading failures. A full disk on a WordPress server does not just stop logging. It stops MySQL from writing, it stops PHP-FPM from creating sessions, and it stops WooCommerce from processing orders.

If you have recently upgraded to PHP 8.4 — or your host did it for you — check your PHP-FPM error log size right now. You might be three days away from a disk full alert of your own.


Stop Firefighting. Start Maintaining.

I manage 70+ WordPress sites for agencies and businesses. Whether you need ongoing maintenance, emergency support, or a one-off performance fix — I can help.

View Maintenance Plans Get in Touch

Stop Firefighting. Start Maintaining.

I manage 70+ WordPress sites for agencies and businesses. Whether you need ongoing maintenance, emergency support, or a one-off performance fix — I can help.

View Maintenance Plans Get in Touch

Get in Touch to Discuss Your Needs