Optimizing LAMP Stack Memory Usage: Solving Apache/PHP Swap Space Exhaustion Issues


10 views

The provided LAMP stack configuration shows aggressive prefork settings combined with relatively low PHP memory limits. Let's break down the key parameters:

# Apache Prefork MPM settings
StartServers       64
MinSpareServers    64
MaxSpareServers    128
ServerLimit        256
MaxClients         256
MaxRequestsPerChild  4096

# PHP settings
max_execution_time = 30
max_input_time = 30
memory_limit = 80M

From the top screenshot analysis (though not visible here), we can infer several critical memory consumption patterns:

  • Numerous Apache processes consuming significant RAM
  • PHP processes hitting the 80MB limit frequently
  • System cache/buffers consuming available memory

The current prefork settings may be too aggressive for typical web servers. Here's a more balanced configuration:


StartServers       8
MinSpareServers    5
MaxSpareServers    10
ServerLimit        150
MaxClients         150
MaxRequestsPerChild  1000

Key adjustments:

  • Reduced initial server count to decrease cold-start memory pressure
  • Tighter spare server range to maintain responsiveness without waste
  • Lowered MaxClients to prevent OOM situations
  • More frequent process recycling to prevent memory leaks

Consider these php.ini adjustments:

memory_limit = 128M  # Increased from 80MB
max_execution_time = 60  # Allows more complex operations
realpath_cache_size = 256k  # Reduces filesystem ops
opcache.enable=1  # Enable opcode caching
opcache.memory_consumption=128  # MB allocated for opcache

Combine this with application-level improvements:

// Example memory-saving technique in PHP
function process_large_data() {
    // Process in chunks
    $chunkSize = 1000;
    $total = 100000;
    
    for ($i = 0; $i < $total; $i += $chunkSize) {
        $data = get_data_chunk($i, $chunkSize);
        process_chunk($data);
        unset($data); // Explicit memory cleanup
        gc_collect_cycles(); // Force garbage collection
    }
}

Implement these Linux kernel parameters in /etc/sysctl.conf:

vm.swappiness = 10  # Reduce tendency to swap
vm.vfs_cache_pressure = 50  # Balance between cache and process memory
vm.dirty_ratio = 10  # Aggressive writeback of dirty pages
vm.dirty_background_ratio = 5  # Background writeback threshold

Apply changes with:

sudo sysctl -p

For high-traffic sites, evaluate these alternatives:

  1. Event MPM (replaces prefork for better concurrency)
  2. PHP-FPM with nginx for more efficient process management
  3. Reverse proxy cache (Varnish or Nginx cache)

Example Event MPM configuration:


    StartServers             2
    MinSpareThreads         25
    MaxSpareThreads         75
    ThreadLimit             64
    ThreadsPerChild         25
    MaxRequestWorkers      150
    MaxConnectionsPerChild   1000

Implement these monitoring solutions:

# Sample cron job for memory monitoring
*/5 * * * * /usr/bin/free -m | awk '/^Mem/ {if ($3/$2 > 0.8) system("/usr/sbin/service apache2 restart")}'

# Install and configure prometheus node exporter
apt install prometheus-node-exporter

Example Grafana dashboard metrics to track:

  • Apache scoreboard states (waiting/reading/sending)
  • PHP process memory usage distribution
  • Swap usage trends over time
  • System cache/buffer ratios

When your LAMP servers start choking on swap space, it's often a configuration mismatch between Apache's process management and PHP's resource limits. The top screenshot reveals classic symptoms: too many Apache processes competing for limited memory resources.

The current prefork configuration creates unnecessary memory pressure:

<IfModule prefork.c>
StartServers       64  # Too aggressive for most workloads
MinSpareServers    64  # Keeps too many idle processes
MaxSpareServers    128 # Wastes memory on standby
ServerLimit        256 # Dangerously high ceiling
MaxClients         256 # Recipe for OOM disasters
MaxRequestsPerChild  4096
</IfModule>

Try this optimized setup for a 4GB RAM server:

<IfModule prefork.c>
StartServers       8
MinSpareServers    5  
MaxSpareServers    10 
ServerLimit        50
MaxClients         50
MaxRequestsPerChild  1000  # Helps prevent memory leaks
</IfModule>

The 80MB memory_limit might be insufficient for modern PHP applications. Consider this graduated approach:

; php.ini adjustments
memory_limit = 128M  # Baseline for most CMS platforms
max_execution_time = 45  # Slightly more breathing room
realpath_cache_size = 256k  # Reduces filesystem overhead
opcache.enable=1  # Critical for performance

Install this quick diagnostic script at /var/www/html/memcheck.php:

<?php
header('Content-Type: text/plain');
echo "Memory usage: " . memory_get_usage()/1024 . "KB\n";
echo "Peak usage: " . memory_get_peak_usage()/1024 . "KB\n";
echo "PHP limit: " . ini_get('memory_limit') . "\n";

$apache = shell_exec('ps -ylC apache2 --sort:rss | awk \'{sum+=$8} END {print sum/1024}\'');
echo "Total Apache RSS: " . $apache . "MB\n";
?>

For high-traffic sites, consider migrating to mpm_event with PHP-FPM:

# Install required modules
sudo apt install apache2 apache2-bin apache2-data apache2-utils libapache2-mod-fcgid libapache2-mod-php

# Sample FPM pool configuration
[www]
user = www-data
group = www-data
listen = /run/php/php7.4-fpm.sock
pm = dynamic
pm.max_children = 30
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10

Modify swappiness to prioritize RAM usage:

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

# Temporary change
sudo sysctl vm.swappiness=10

# Permanent setting
echo "vm.swappiness=10" >> /etc/sysctl.conf