Diagnose Apache memory issues and optimize MPM modules, worker processes, and configuration to reduce RAM usage and improve web server performance.
Prerequisites
- Root access to the server
- Apache web server installed
- Basic command line knowledge
What this solves
Apache web servers can consume excessive memory due to misconfigured Multi-Processing Modules (MPM), too many worker processes, or unnecessary modules. This guide helps you diagnose high memory usage, tune MPM settings, disable unused modules, and implement monitoring to keep Apache running efficiently.
Diagnose Apache memory usage
Check current Apache processes
Start by examining how much memory Apache is currently using across all processes.
ps aux | grep apache2 | grep -v grep
ps aux | grep httpd | grep -v grep
Calculate total Apache memory consumption
Get a precise measurement of Apache's total memory footprint using process monitoring tools.
pmap -d $(pgrep apache2) 2>/dev/null | tail -1
pmap -d $(pgrep httpd) 2>/dev/null | tail -1
Monitor memory usage in real-time
Use htop to watch Apache processes and their memory consumption patterns over time.
sudo htop -p $(pgrep -d',' apache2)
sudo htop -p $(pgrep -d',' httpd)
You can also use our comprehensive process monitoring guide for more advanced analysis techniques.
Check Apache configuration and active modules
Identify which MPM module is active and what modules are loaded.
apache2ctl -V | grep -i mpm
apache2ctl -M | head -20
Configure MPM modules for memory optimization
Switch to Event MPM for better memory efficiency
The Event MPM handles more concurrent connections with fewer processes than Prefork MPM, significantly reducing memory usage.
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
sudo systemctl restart apache2
Configure Event MPM parameters
Tune the Event MPM settings based on your server's available memory and expected traffic load.
StartServers 3
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 400
ServerLimit 16
AsyncRequestWorkerFactor 2
Calculate optimal MaxRequestWorkers
Set MaxRequestWorkers based on available server memory to prevent swap usage.
# Calculate: (Available RAM - Other processes) / Average Apache process size
For a 4GB server with 1GB for other processes:
(3GB * 1024MB) / 25MB per process = ~122 workers
free -m
echo "Recommended MaxRequestWorkers: $(($(free -m | awk '/^Mem:/{print $2}') * 3 / 4 / 25))"
Disable unnecessary Apache modules
List all enabled modules
Identify modules that are loaded but not needed for your specific use case.
apache2ctl -M | sort
Disable common unnecessary modules
Disable modules that are commonly enabled by default but rarely used in production.
sudo a2dismod status
sudo a2dismod autoindex
sudo a2dismod negotiation
sudo a2dismod include
sudo systemctl reload apache2
Keep only essential modules
Ensure you keep modules required for basic functionality and your specific applications.
Optimize Apache configuration
Configure KeepAlive settings
Tune KeepAlive to reduce connection overhead while preventing connection hoarding.
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 2
Set appropriate timeout values
Configure timeouts to prevent processes from hanging and consuming memory indefinitely.
Timeout 30
RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
Enable compression to reduce bandwidth usage
Configure mod_deflate to compress responses and reduce memory spent on large transfers.
sudo a2enmod deflate
sudo systemctl reload apache2
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
Implement memory monitoring and alerts
Create memory monitoring script
Set up automated monitoring to track Apache memory usage and alert when thresholds are exceeded.
#!/bin/bash
Apache process names (adjust based on your distribution)
APACHE_PROC="apache2\|httpd"
Memory threshold in MB
MEMORY_THRESHOLD=1000
Calculate total Apache memory usage
TOTAL_MEMORY=$(ps aux | grep "$APACHE_PROC" | grep -v grep | awk '{sum+=$6} END {print sum/1024}')
TOTAL_MEMORY=${TOTAL_MEMORY%.*} # Remove decimal
if [ "$TOTAL_MEMORY" -gt "$MEMORY_THRESHOLD" ]; then
echo "WARNING: Apache using ${TOTAL_MEMORY}MB (threshold: ${MEMORY_THRESHOLD}MB)"
echo "Apache processes:"
ps aux | grep "$APACHE_PROC" | grep -v grep
# Add your alerting logic here (email, webhook, etc.)
else
echo "Apache memory usage: ${TOTAL_MEMORY}MB (OK)"
fi
sudo chmod +x /usr/local/bin/apache-memory-monitor.sh
Schedule regular memory checks
Use cron to run memory checks every 5 minutes and log the results.
sudo crontab -e
# Apache memory monitoring every 5 minutes
/5 * /usr/local/bin/apache-memory-monitor.sh >> /var/log/apache-memory.log 2>&1
Set up log rotation for monitoring logs
Prevent monitoring logs from consuming excessive disk space.
/var/log/apache-memory.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
}
Apply kernel-level memory optimizations
Configure system memory settings
Optimize kernel parameters to better handle web server memory patterns.
# Reduce swap usage (good for web servers with enough RAM)
vm.swappiness=10
Increase file handle limits for high-traffic servers
fs.file-max=65536
Optimize TCP memory for web connections
net.ipv4.tcp_mem=65536 131072 262144
net.core.rmem_max=16777216
net.core.wmem_max=16777216
sudo sysctl -p /etc/sysctl.d/99-apache-memory.conf
For more advanced kernel tuning, see our comprehensive sysctl optimization guide.
Increase system limits for Apache
Configure systemd to allow Apache to use more file descriptors and memory.
[Service]
LimitNOFILE=65536
LimitNPROC=4096
sudo mkdir -p /etc/systemd/system/apache2.service.d
sudo systemctl daemon-reload
sudo systemctl restart apache2
Verify your setup
Test Apache configuration
Ensure all configuration changes are syntactically correct before restarting.
sudo apache2ctl configtest
sudo systemctl restart apache2
sudo systemctl status apache2
Monitor memory usage after optimization
Compare memory usage before and after your changes to measure improvement.
# Check current memory usage
ps aux | grep -E "apache2|httpd" | grep -v grep | awk '{sum+=$6} END {print "Total Apache memory: " sum/1024 " MB"}'
Test server performance under load
Use Apache's built-in benchmarking tool to verify performance improvements.
ab -n 1000 -c 10 http://localhost/
Monitor memory during the test:
watch 'ps aux | grep -E "apache2|httpd" | grep -v grep'
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Apache won't start after MPM change | PHP module incompatible with Event MPM | Install libapache2-mod-php-fpm and configure PHP-FPM instead |
| Still high memory usage | Memory leaks in PHP applications | Enable PHP-FPM with pm.max_requests = 500 to restart workers periodically |
| Connection timeouts increased | KeepAliveTimeout too low | Increase to 5 seconds for slower clients |
| Configuration test fails | Missing required modules | Re-enable essential modules like mod_mime and mod_dir |
| High swap usage | MaxRequestWorkers set too high | Reduce to fit available RAM: (RAM - 1GB) / 25MB |
| 502 errors with PHP-FPM | FPM socket permissions | Set listen.owner = www-data in FPM pool config |
Next steps
- Enable HTTP/2 for better performance
- Add ModSecurity web application firewall
- Implement rate limiting and DDoS protection
- Set up comprehensive Apache monitoring
- Implement SSL security hardening
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Apache Memory Optimization Script
# Optimizes Apache web server for better memory usage and performance
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Global variables
APACHE_SERVICE=""
APACHE_CTL=""
APACHE_CONF_DIR=""
SITES_DIR=""
MODS_DIR=""
BACKUP_DIR="/tmp/apache_optimization_backup_$(date +%Y%m%d_%H%M%S)"
# Usage function
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -m, --max-workers NUM Set MaxRequestWorkers (default: auto-calculate)"
echo " -h, --help Show this help message"
exit 1
}
# Error handling
cleanup() {
if [[ $? -ne 0 ]]; then
echo -e "${RED}[ERROR] Script failed. Restoring backup from $BACKUP_DIR${NC}"
if [[ -d "$BACKUP_DIR" ]]; then
cp -r "$BACKUP_DIR"/* "$APACHE_CONF_DIR"/ 2>/dev/null || true
systemctl reload "$APACHE_SERVICE" 2>/dev/null || true
fi
fi
}
trap cleanup ERR
# Logging functions
log_info() { echo -e "${GREEN}$1${NC}"; }
log_warn() { echo -e "${YELLOW}$1${NC}"; }
log_error() { echo -e "${RED}$1${NC}"; }
# Check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root or with sudo"
exit 1
fi
}
# Detect distribution and set package manager
detect_distro() {
if [[ ! -f /etc/os-release ]]; then
log_error "Cannot detect distribution. /etc/os-release not found."
exit 1
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
APACHE_SERVICE="apache2"
APACHE_CTL="apache2ctl"
APACHE_CONF_DIR="/etc/apache2"
SITES_DIR="/etc/apache2/sites-available"
MODS_DIR="/etc/apache2/mods-available"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf check-update"
APACHE_SERVICE="httpd"
APACHE_CTL="httpd"
APACHE_CONF_DIR="/etc/httpd"
SITES_DIR="/etc/httpd/conf.d"
MODS_DIR="/etc/httpd/conf.modules.d"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum check-update"
APACHE_SERVICE="httpd"
APACHE_CTL="httpd"
APACHE_CONF_DIR="/etc/httpd"
SITES_DIR="/etc/httpd/conf.d"
MODS_DIR="/etc/httpd/conf.modules.d"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
}
# Create backup
create_backup() {
log_info "[2/8] Creating configuration backup..."
mkdir -p "$BACKUP_DIR"
cp -r "$APACHE_CONF_DIR"/* "$BACKUP_DIR"/
log_info "Backup created at $BACKUP_DIR"
}
# Install required packages
install_packages() {
log_info "[3/8] Installing required packages..."
$PKG_UPDATE >/dev/null 2>&1 || true
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL htop procps >/dev/null
else
$PKG_INSTALL htop procps-ng >/dev/null
if ! command -v httpd >/dev/null; then
$PKG_INSTALL httpd httpd-devel >/dev/null
fi
fi
}
# Calculate optimal MaxRequestWorkers
calculate_max_workers() {
local total_mem=$(free -m | awk '/^Mem:/{print $2}')
local available_mem=$((total_mem * 3 / 4))
local avg_process_size=25
echo $((available_mem / avg_process_size))
}
# Configure Event MPM
configure_event_mpm() {
log_info "[4/8] Configuring Event MPM module..."
local max_workers=${MAX_WORKERS:-$(calculate_max_workers)}
local server_limit=$((max_workers / 25 + 1))
if [[ "$PKG_MGR" == "apt" ]]; then
# Disable prefork, enable event
a2dismod mpm_prefork >/dev/null 2>&1 || true
a2enmod mpm_event >/dev/null 2>&1
# Configure Event MPM
cat > "$APACHE_CONF_DIR/mods-available/mpm_event.conf" << EOF
<IfModule mpm_event_module>
StartServers 3
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers $max_workers
ServerLimit $server_limit
AsyncRequestWorkerFactor 2
</IfModule>
EOF
else
# RHEL-based systems
sed -i 's/^LoadModule mpm_prefork_module/#LoadModule mpm_prefork_module/' "$MODS_DIR/00-mpm.conf" 2>/dev/null || true
sed -i 's/^#LoadModule mpm_event_module/LoadModule mpm_event_module/' "$MODS_DIR/00-mpm.conf" 2>/dev/null || true
cat > "$APACHE_CONF_DIR/conf.d/mpm_event.conf" << EOF
<IfModule mpm_event_module>
StartServers 3
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers $max_workers
ServerLimit $server_limit
AsyncRequestWorkerFactor 2
</IfModule>
EOF
fi
log_info "Event MPM configured with MaxRequestWorkers: $max_workers"
}
# Disable unnecessary modules
disable_modules() {
log_info "[5/8] Disabling unnecessary modules..."
local modules_to_disable=("status" "autoindex" "negotiation" "include")
if [[ "$PKG_MGR" == "apt" ]]; then
for module in "${modules_to_disable[@]}"; do
a2dismod "$module" >/dev/null 2>&1 || true
done
else
# Comment out modules in RHEL-based systems
for module in "${modules_to_disable[@]}"; do
sed -i "s/^LoadModule ${module}_module/#LoadModule ${module}_module/" "$MODS_DIR"/*.conf 2>/dev/null || true
done
fi
}
# Optimize Apache configuration
optimize_apache_config() {
log_info "[6/8] Optimizing Apache configuration..."
local config_file=""
if [[ "$PKG_MGR" == "apt" ]]; then
config_file="$APACHE_CONF_DIR/apache2.conf"
else
config_file="$APACHE_CONF_DIR/conf/httpd.conf"
fi
# Add optimization settings
cat >> "$config_file" << EOF
# Memory and Performance Optimizations
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
# Security and Performance Headers
ServerTokens Prod
ServerSignature Off
# Timeout Settings
Timeout 60
RequestReadTimeout 60
# Limit request size (adjust as needed)
LimitRequestBody 10485760
EOF
# Set proper permissions
chown root:root "$config_file"
chmod 644 "$config_file"
}
# Setup monitoring
setup_monitoring() {
log_info "[7/8] Setting up monitoring script..."
cat > /usr/local/bin/apache-memory-monitor.sh << 'EOF'
#!/bin/bash
APACHE_PROC="apache2"
command -v httpd >/dev/null && APACHE_PROC="httpd"
echo "Apache Memory Usage Report - $(date)"
echo "=================================="
ps aux | grep $APACHE_PROC | grep -v grep | awk '{sum+=$6} END {print "Total Memory: " sum/1024 " MB"}'
echo "Active processes: $(pgrep $APACHE_PROC | wc -l)"
echo "Top 5 memory consuming Apache processes:"
ps aux | grep $APACHE_PROC | grep -v grep | sort -k6 -nr | head -5 | awk '{print $6/1024 " MB - PID " $2}'
EOF
chmod 755 /usr/local/bin/apache-memory-monitor.sh
chown root:root /usr/local/bin/apache-memory-monitor.sh
log_info "Monitoring script installed at /usr/local/bin/apache-memory-monitor.sh"
}
# Restart and verify
restart_and_verify() {
log_info "[8/8] Restarting Apache and verifying configuration..."
# Test configuration
if ! $APACHE_CTL configtest >/dev/null 2>&1; then
log_error "Configuration test failed. Restoring backup..."
cp -r "$BACKUP_DIR"/* "$APACHE_CONF_DIR"/
exit 1
fi
# Restart Apache
systemctl restart "$APACHE_SERVICE"
systemctl enable "$APACHE_SERVICE" >/dev/null 2>&1
# Verify Apache is running
if systemctl is-active --quiet "$APACHE_SERVICE"; then
log_info "Apache successfully optimized and running!"
# Show current MPM
echo "Current MPM: $($APACHE_CTL -V | grep -i mpm || echo 'Could not detect MPM')"
echo "Active processes: $(pgrep apache2 httpd 2>/dev/null | wc -l)"
echo "Run '/usr/local/bin/apache-memory-monitor.sh' to monitor memory usage"
else
log_error "Apache failed to start after optimization"
exit 1
fi
}
# Main function
main() {
local MAX_WORKERS=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-m|--max-workers)
MAX_WORKERS="$2"
shift 2
;;
-h|--help)
usage
;;
*)
log_error "Unknown option: $1"
usage
;;
esac
done
log_info "[1/8] Starting Apache memory optimization..."
check_root
detect_distro
create_backup
install_packages
configure_event_mpm
disable_modules
optimize_apache_config
setup_monitoring
restart_and_verify
log_info "Apache optimization completed successfully!"
}
main "$@"
Review the script before running. Execute with: bash install.sh