When dealing with 50k+ visits on a resource-constrained VPS running Apache/PHP/MySQL stack, the performance degradation typically stems from multiple compounding factors. Here's how to systematically address them:
# In httpd.conf or apache2.conf
StartServers 2
MinSpareServers 2
MaxSpareServers 5
ServerLimit 50
MaxClients 50
MaxRequestsPerChild 1000
KeepAlive Off
Timeout 30
Key rationale: On a 512MB VPS, you need to balance between accepting connections and leaving room for MySQL. Setting MaxClients too high will cause swapping.
; php.ini adjustments
realpath_cache_size = 256k
realpath_cache_ttl = 3600
opcache.enable=1
opcache.memory_consumption=64
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
For ExpressionEngine specifically, enable template caching and disable tracking if not needed:
# In config.php
$config['enable_sql_caching'] = 'y';
$config['template_debugging'] = 'n';
$config['save_tmpl_files'] = 'y';
# my.cnf for 512MB system
[mysqld]
key_buffer_size = 32M
query_cache_size = 32M
tmp_table_size = 16M
max_heap_table_size = 16M
innodb_buffer_pool_size = 64M
innodb_log_file_size = 16M
table_open_cache = 400
table_definition_cache = 400
Critical indexes for ExpressionEngine tables:
ALTER TABLE exp_channel_titles ADD INDEX (entry_date);
ALTER TABLE exp_comments ADD INDEX (entry_id);
ALTER TABLE exp_category_posts ADD INDEX (entry_id);
When dealing with extreme traffic spikes, consider these additional measures:
# Replace Apache with Nginx + PHP-FPM
server {
listen 80;
server_name example.com;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Essential commands for real-time diagnostics:
# Apache status
apachectl status
# MySQL queries
mysqladmin processlist
# Memory usage
free -m
# Disk I/O
iostat -x 1
# CPU usage
mpstat -P ALL 1
Beyond configuration files:
- Enable static caching for high-traffic templates
- Minimize template conditionals
- Use {embed:save} for reusable components
- Disable member tracking if not used
Example template caching:
{exp:channel:entries channel="news" limit="10" dynamic="no" cache="yes" refresh="60"}
<h3>{title}</h3>
{/exp:channel:entries}
When dealing with a 512MB RAM VPS running ExpressionEngine CMS, we need to make surgical optimizations. The key symptoms (30s+ response times under 50k visits) suggest three main bottlenecks:
# Current Apache MPM settings (check with):
apache2ctl -V | grep -i mpm
# Typical output for low-memory VPS:
Server MPM: prefork
Switch to event MPM if possible (requires PHP-FPM):
# Install required modules
sudo apt install apache2-mpm-event php-fpm
# Sample /etc/apache2/mods-available/mpm_event.conf
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 150
MaxConnectionsPerChild 1000
For ExpressionEngine specifically, configure PHP-FPM pool:
# /etc/php/7.4/fpm/pool.d/www.conf (adjust version)
[www]
pm = dynamic
pm.max_children = 15
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
php_admin_value[memory_limit] = 64M
Critical for 512MB systems - this my.cnf works for EE:
# /etc/mysql/my.cnf
[mysqld]
skip-name-resolve
key_buffer_size = 32M
query_cache_size = 16M
tmp_table_size = 16M
max_heap_table_size = 16M
innodb_buffer_pool_size = 64M
innodb_log_file_size = 16M
table_open_cache = 400
thread_cache_size = 8
max_connections = 30
Implement these in config.php:
$config['enable_sql_caching'] = 'y';
$config['disable_all_tracking'] = 'y';
$config['template_debugging'] = 'n';
$config['save_tmpl_files'] = 'y'; // Requires ./templates cache dir
$config['save_tmpl_revisions'] = 'n';
$config['enable_emoticons'] = 'n';
Essential sysctl tweaks:
# /etc/sysctl.conf
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_tw_reuse = 1
net.core.somaxconn = 1024
vm.swappiness = 10
vm.vfs_cache_pressure = 50
Create this bash script to identify bottlenecks:
#!/bin/bash
while true; do
echo -e "\n$(date)"
echo "Apache Processes: $(ps aux | grep apache2 | wc -l)"
echo "PHP-FPM Processes: $(ps aux | grep php-fpm | wc -l)"
echo "MySQL Processes: $(mysqladmin processlist | wc -l)"
free -m
sleep 5
done