• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Step-by-Step: Diagnosing Out of Memory (OOM) Killer terminating PHP-FPM pool workers on DigitalOcean Servers

Step-by-Step: Diagnosing Out of Memory (OOM) Killer terminating PHP-FPM pool workers on DigitalOcean Servers

Identifying the OOM Killer’s Handiwork

When your PHP-FPM pool workers are being unceremoniously terminated, and you suspect the Linux Out-of-Memory (OOM) Killer is the culprit, the first step is to confirm its involvement. The OOM Killer logs its actions to the system journal. On most modern Linux distributions, including those typically found on DigitalOcean, you can query this journal using journalctl.

A common pattern to look for is messages indicating a process was “killed by OOM killer”. You’ll want to filter these messages to focus on your PHP-FPM processes. The executable name for PHP-FPM is usually php-fpm or php-fpm[version]. You can also look for the specific process ID (PID) if you have it, but filtering by process name is often more practical.

Querying the System Journal for OOM Events

Execute the following command to search the system journal for OOM killer events related to PHP-FPM. This command searches for messages containing “Out of memory” or “killed process” and filters them to include lines where “php-fpm” is present. The -k flag searches for kernel messages, and -g is used for general search terms.

To get a more focused view, especially if you have multiple PHP versions or other processes that might trigger OOM, you can specify a time range. For instance, to look at the last hour:

sudo journalctl -k -g "Out of memory" --since "1 hour ago" | grep php-fpm
sudo journalctl -k -g "killed process" --since "1 hour ago" | grep php-fpm

If you find entries similar to the following, the OOM killer is indeed terminating your PHP-FPM workers:

[... timestamp ...] kernel: Out of memory: Kill process [PID] ([php-fpm process name]) score [score] or sacrifice child
[... timestamp ...] kernel: Killed process [PID] ([php-fpm process name]) , UID [UID] , total-vm: [VM size]kB, anon-rss: [RSS size]kB, file-rss: [file RSS size]kB

Understanding Memory Usage Metrics

Once OOM killer involvement is confirmed, the next step is to understand *why* your PHP-FPM workers are consuming so much memory. This involves inspecting the memory usage of individual PHP-FPM processes and the overall system memory. Tools like top, htop, and ps are invaluable here.

1. System-wide Memory Usage:

free -h

This command provides a human-readable overview of your system’s RAM and swap usage. Pay close attention to the “available” memory and the swap usage. High swap usage indicates the system is already under memory pressure.

2. PHP-FPM Process Memory Usage:

ps aux --sort=-%mem | grep php-fpm

This command lists all processes, sorts them by memory usage in descending order, and filters for PHP-FPM. Look at the %MEM and RSS (Resident Set Size) columns. The VSZ (Virtual Memory Size) can also be informative, but RSS is a more direct indicator of actual physical memory being used.

3. Real-time Monitoring with htop:

htop

htop offers an interactive, real-time view of processes. You can sort by memory usage (press F6 and select PERCENT_MEM) and easily identify which PHP-FPM worker processes are consuming the most memory. Look for spikes in memory usage that correlate with the OOM events.

Analyzing PHP-FPM Configuration

The configuration of your PHP-FPM pool is a primary suspect for excessive memory consumption. DigitalOcean servers, especially smaller droplets, have limited RAM. Aggressive pool settings can quickly exhaust this memory.

Locate your PHP-FPM pool configuration file. This is typically found in directories like /etc/php/[version]/fpm/pool.d/www.conf or similar. Open this file and examine the following directives:

; pm = dynamic
; pm.max_children = 35
; pm.start_servers = 10
; pm.min_spare_servers = 5
; pm.max_spare_servers = 20
; pm.process_idle_timeout = 10s
; request_terminate_timeout = 30s
; pm.max_requests = 500

Key Directives and Their Impact:

  • pm: Process Manager. Common values are static, dynamic, and ondemand. dynamic is often a good balance, but aggressive settings can still cause issues.
  • pm.max_children: This is the most critical setting. It defines the maximum number of child processes that can be spawned simultaneously. If your server has 1GB of RAM, and each PHP-FPM worker (plus the web server, database, etc.) consumes 100MB, you can only sustain about 10 workers. Setting this too high is a direct path to OOM.
  • pm.start_servers: The number of child processes to start when the master process is started.
  • pm.min_spare_servers: The minimum number of idle respawned processes.
  • pm.max_spare_servers: The maximum number of idle respawned processes.
  • pm.max_requests: The number of requests each child process will execute before respawning. Setting this too low can lead to frequent respawning and overhead; setting it too high can lead to memory leaks accumulating over time.
  • request_terminate_timeout: The number of seconds after which a script will be terminated. Long-running scripts can tie up workers and consume memory.

Calculation Example:

Let’s say your DigitalOcean droplet has 2GB of RAM. The OS and other services (Nginx, MySQL, etc.) might consume 500MB. This leaves 1.5GB (1536MB) for PHP-FPM. If each PHP-FPM worker, on average, uses 150MB of RAM (including interpreter, loaded extensions, and script execution), then:

1536MB / 150MB per worker ≈ 10.24 workers

In this scenario, setting pm.max_children to 35 (as in the example config) is a recipe for disaster. You should aim for a value significantly lower, perhaps 8-10, and monitor closely.

Investigating PHP Script Memory Leaks

Even with conservative PHP-FPM settings, individual PHP scripts can be memory hogs. This is often due to inefficient coding practices, such as loading large datasets into memory, recursive functions without proper termination, or unclosed resources.

1. Enabling PHP’s Memory Limit:

Ensure your php.ini file has a reasonable memory_limit set. While this won’t prevent OOM killer from terminating the *process*, it can help identify scripts that are individually exceeding a defined threshold before they bring down the whole system.

memory_limit = 128M ; Adjust as needed

You can check your active php.ini file using:

php -i | grep "Loaded Configuration File"
php -i | grep "memory_limit"

2. Profiling PHP Scripts:

For deeper analysis, use a PHP profiler like Xdebug or Blackfire.io. These tools can pinpoint exactly which functions or lines of code are consuming the most memory within a script’s execution.

Using Xdebug:

Configure Xdebug to collect memory usage profiles. You can then analyze these profiles with tools like KCacheGrind (on Linux/macOS) or Webgrind (web-based).

; In php.ini or xdebug.ini
xdebug.mode = profile
xdebug.output_dir = /tmp/xdebug_profiles
xdebug.profile_enable_trigger = 1 ; Enable profiling via a trigger, e.g., GET/POST parameter
xdebug.collect_memory_garbage_statistics = 1

When a script is suspected of high memory usage, trigger Xdebug profiling (e.g., by adding XDEBUG_PROFILE=1 to your request URL) and then analyze the generated `.prof` file in /tmp/xdebug_profiles.

System-Level Tuning and Optimization

Beyond PHP-FPM configuration, several system-level adjustments can help mitigate OOM issues.

1. Swappiness:

The swappiness kernel parameter controls how aggressively the system uses swap space. A high value means it will swap out processes more readily, which can sometimes prevent OOM kills but also lead to performance degradation. A low value prioritizes keeping processes in RAM.

# Check current swappiness
cat /proc/sys/vm/swappiness

# Temporarily set swappiness (e.g., to 10)
sudo sysctl vm.swappiness=10

# Make it permanent
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.conf

For memory-constrained servers, a lower swappiness might be beneficial, but it’s a trade-off. If you’re still hitting OOM with low swappiness, it indicates a fundamental lack of RAM for the workload.

2. Adjusting OOM Score Adjustments:

The OOM killer uses a heuristic to decide which process to kill, based on an “oom_score”. Processes that are large, long-running, and not critical tend to have higher scores. You can influence this score for specific processes.

You can find the OOM score adjustment for a process by looking in /proc/[PID]/oom_score_adj. A value of -1000 will prevent the process from being killed by the OOM killer. However, this is generally discouraged as it can lead to the system becoming unresponsive if critical processes are protected while others are starved.

# Example: Prevent a specific PHP-FPM worker (PID 12345) from being killed
# This is generally NOT recommended for production unless you fully understand the implications.
echo -1000 | sudo tee /proc/12345/oom_score_adj

A more practical approach is to ensure that essential system processes have a lower (more negative) oom_score_adj than your application processes. This is usually handled by the system’s init system (systemd).

When to Scale Up

If, after thorough analysis and tuning, your PHP-FPM pool workers continue to be terminated by the OOM killer, it’s a strong indicator that your current server resources are insufficient for your application’s demands. This is the point where scaling up your DigitalOcean droplet (increasing RAM) or scaling out (distributing the load across multiple servers) becomes necessary.

Before scaling, ensure you have exhausted all optimization possibilities. However, remember that sometimes, the most efficient solution is simply more resources. Monitor your server’s memory usage closely after any configuration changes or scaling operations.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Zend Lifecycles: Utilizing Extension Hooks (MINIT, RINIT, RSHUTDOWN, MSHUTDOWN) for Resource Cleaning
  • Build Automation: Creating PHP Custom Extensions via phpize, config.m4, and Makefiles
  • JIT Compiler vs. C Extensions: Analyzing Execution Speedups in PHP 8 Native JIT vs. Compiled C Modules
  • CodeIgniter 4 vs. Laravel: High-Performance Micro-Router Architecture vs. Rich Service-Provider Monoliths
  • Flask vs. Django: Micro-Framework Custom Extensions vs. Batteries-Included Enterprise Monoliths

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (583)
  • DevOps (7)
  • DevOps & Cloud Scaling (956)
  • Django (1)
  • Laravel (1)
  • Migration & Architecture (192)
  • MySQL (1)
  • Performance & Optimization (783)
  • PHP (5)
  • PHP Development (5)
  • Plugins & Themes (244)
  • Programming Languages (1)
  • Python (3)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Server (23)
  • Ubuntu (9)
  • Web Applications & Frontend (1)
  • WordPress (22)
  • WordPress Plugin Development (7)
  • WordPress Theme Development (355)

Recent Posts

  • Zend Lifecycles: Utilizing Extension Hooks (MINIT, RINIT, RSHUTDOWN, MSHUTDOWN) for Resource Cleaning
  • Build Automation: Creating PHP Custom Extensions via phpize, config.m4, and Makefiles
  • JIT Compiler vs. C Extensions: Analyzing Execution Speedups in PHP 8 Native JIT vs. Compiled C Modules
  • CodeIgniter 4 vs. Laravel: High-Performance Micro-Router Architecture vs. Rich Service-Provider Monoliths
  • Flask vs. Django: Micro-Framework Custom Extensions vs. Batteries-Included Enterprise Monoliths
  • Express vs. NestJS: Raw Middleware Handlers vs. Strict TypeScript Dependency-Injecting OOP Modules

Top Categories

  • DevOps & Cloud Scaling (956)
  • Performance & Optimization (783)
  • Debugging & Troubleshooting (583)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Business & Monetization (390)

Our Products

  • School Management & Student Administration System
  • Integrated Hospital & Clinic Management System
  • Real Estate Directory & Agent Portal
  • Restaurant POS & Table Booking System
  • Retail Inventory POS & Billing System
  • Pharmacy Inventory & Clinic Billing System

Our Services

  • Vibe Engineering & AI Code Auditing Services
  • Prompt Engineering & "Vibe Coding" Workflow Consulting
  • AI-Augmented "Vibe Coding" & Rapid MVP Development
  • Figma to Shopify Liquid Theme Customization
  • Figma to WooCommerce Frontend Development
  • Figma to Magento 2 Theme Development

Copyright © 2026 · Vinay Vengala