Why DirectAdmin Servers Run Slow: Root Causes
A slow website on a DirectAdmin server is rarely caused by a single bottleneck. The most common culprits are Apache MPM misconfiguration (too few or too many worker processes), PHP-FPM pool exhaustion, an under-tuned MySQL InnoDB buffer pool, and missing opcode caching. When all four are suboptimal simultaneously — as they are on a freshly installed DirectAdmin server — the combined effect can make even a simple WordPress site feel sluggish under moderate load.
This guide covers the complete performance stack for DirectAdmin: Apache MPM Event, PHP-FPM pool tuning, OPcache, MySQL/MariaDB InnoDB settings, and Nginx as a reverse proxy front-end — with real configuration values you can apply today.
Step 1: Identify the Bottleneck Before Tuning
Before changing any configuration, measure where time is actually being spent.
Check Server Load and Memory
# Current load average (1, 5, 15 minute)
uptime
# Output: load average: 4.21, 3.89, 3.51
# Rule of thumb: load > number of CPU cores = overloaded
# Memory breakdown
free -h
# Check if swap is being used heavily — that alone causes severe slowness
# Top processes consuming CPU/memory
top -bn1 | head -20
# Apache process count
ps aux | grep -c httpd
ps aux | grep -c apache2
Check MySQL Slow Query Log
# Enable slow query log temporarily
mysql -e "SET GLOBAL slow_query_log = 'ON';"
mysql -e "SET GLOBAL long_query_time = 1;"
mysql -e "SET GLOBAL slow_query_log_file = '/var/log/mysql-slow.log';"
# After 10 minutes, analyze the log
mysqldumpslow -t 10 /var/log/mysql-slow.log
Time a PHP Response
# Measure TTFB (Time To First Byte) from the server itself
curl -o /dev/null -s -w "Connect: %{time_connect}s | TTFB: %{time_starttransfer}s | Total: %{time_total}s
" https://yourdomain.com/
# Target: TTFB under 200ms for cached pages, under 800ms for dynamic PHP
Step 2: Switch Apache to MPM Event Mode
DirectAdmin's default Apache setup often uses MPM Prefork, which spawns a new process for every request — extremely memory-hungry. MPM Event uses threads and is far more efficient for high-concurrency loads.
# Check current MPM
httpd -V | grep -i mpm
# or
apache2 -V | grep -i mpm
# Switch MPM in CustomBuild (DirectAdmin's build system)
cd /usr/local/directadmin/custombuild
./build set apache_mpm event
./build apache
# Verify the switch
httpd -V | grep -i "MPM Name"
# Should show: Server MPM: event
Tune MPM Event Settings
Edit /etc/httpd/conf/extra/httpd-mpm.conf (or /etc/apache2/mods-enabled/mpm_event.conf on Debian):
<IfModule mpm_event_module>
# Tune based on: RAM / (avg PHP-FPM process size in MB)
# For a 4GB RAM server with ~40MB average PHP process:
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 150
MaxConnectionsPerChild 1000
</IfModule>
After editing, test and reload:
apachectl configtest
service httpd graceful
Step 3: Tune PHP-FPM Pool Settings
PHP-FPM is the recommended PHP handler for DirectAdmin with MPM Event. The key configuration is pm.max_children — the maximum number of simultaneous PHP processes. Too low and requests queue; too high and the server runs out of RAM.
Calculate the Right pm.max_children
# Find average PHP-FPM process memory usage
ps --no-headers -o rss -C php-fpm | awk '{sum+=$1} END {print "Average PHP-FPM RSS: " sum/NR/1024 " MB"}'
# Calculate max_children:
# max_children = (Available RAM for PHP) / (Average PHP process size)
# Example: 3000 MB available / 50 MB per process = 60 max_children
Apply PHP-FPM Pool Configuration
PHP-FPM pool configs in DirectAdmin live at /usr/local/php{VERSION}/etc/php-fpm.d/. Edit the pool file for your PHP version:
[www]
; Process manager: use 'dynamic' for most setups
pm = dynamic
; Total PHP processes (calculated above)
pm.max_children = 60
; Start with this many workers
pm.start_servers = 10
; Keep at least this many idle workers
pm.min_spare_servers = 5
; Keep at most this many idle workers
pm.max_spare_servers = 20
; Recycle workers after this many requests (prevents memory leaks)
pm.max_requests = 500
; Kill workers idle for this long (seconds)
pm.process_idle_timeout = 10s
; Log slow requests (PHP execution > 5s)
slowlog = /var/log/php-fpm-slow.log
request_slowlog_timeout = 5s
# Restart PHP-FPM
service php-fpm restart
# or for a specific version
service php74-fpm restart
# Verify workers are running
php-fpm -t && service php-fpm status
Step 4: Enable and Tune OPcache
OPcache compiles PHP files to bytecode and stores them in shared memory, eliminating the parsing overhead on every request. On a WordPress site, enabling OPcache typically cuts PHP execution time by 30–60%.
# Check if OPcache is active
php -r "var_dump(function_exists('opcache_get_status'));"
# Should return: bool(true)
# View current OPcache status
php -r "print_r(opcache_get_status());"
Optimal OPcache Settings
Edit /etc/php.d/10-opcache.ini or /usr/local/php{VERSION}/lib/php.ini:
opcache.enable=1
opcache.enable_cli=0
; Memory for compiled bytecode (MB) — increase if you host many sites
opcache.memory_consumption=256
; Number of cached files — 10000 covers most setups
opcache.max_accelerated_files=10000
; Seconds before checking for source file changes (0 = never check in prod)
opcache.revalidate_freq=60
; Interned string memory (MB)
opcache.interned_strings_buffer=16
; Enable JIT compiler (PHP 8.0+) — significant boost for compute-heavy PHP
opcache.jit_buffer_size=100M
opcache.jit=1255
# Restart PHP-FPM to apply
service php-fpm restart
# Verify OPcache is using memory
php -r "print_r(opcache_get_status()['memory_usage']);"
Step 5: Tune MySQL/MariaDB InnoDB Settings
MySQL performance issues are responsible for slow page load times on database-heavy sites (WordPress, Magento, WooCommerce). The two most impactful settings are innodb_buffer_pool_size and innodb_log_file_size.
# Check current InnoDB buffer pool usage
mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';"
# Check buffer pool hit rate (should be > 99%)
mysql -e "SELECT (1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)) * 100 AS buffer_hit_rate FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME IN ('Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests');" 2>/dev/null || mysql -e "SELECT (1 - (SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME='Innodb_buffer_pool_reads') / (SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests')) * 100 AS hit_rate;"
Apply MySQL Performance Settings
Edit /etc/my.cnf under [mysqld]:
[mysqld]
# Set to 50-70% of available RAM
# For a 4GB server: ~2.5GB
innodb_buffer_pool_size = 2560M
# For servers with >1GB buffer pool, use multiple instances
innodb_buffer_pool_instances = 4
# Larger log file = fewer flushes = better write performance
innodb_log_file_size = 512M
# How often InnoDB flushes logs to disk (2 = flush once per second, less safe but faster)
innodb_flush_log_at_trx_commit = 2
# Use O_DIRECT to avoid double buffering with OS cache
innodb_flush_method = O_DIRECT
# Enable query cache (deprecated in MySQL 8 — skip if using 8.0+)
# query_cache_type = 1
# query_cache_size = 64M
# Connection limits
max_connections = 200
thread_cache_size = 50
# Restart MySQL
service mysqld restart
# or
service mariadb restart
# Verify the settings took effect
mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
Step 6: Add Nginx as a Reverse Proxy Front-End
DirectAdmin's CustomBuild supports running Nginx in front of Apache. Nginx serves static files (images, CSS, JS) with near-zero overhead while Apache handles PHP behind it. This can reduce server load by 40–70% on content-heavy sites.
cd /usr/local/directadmin/custombuild
# Enable Nginx reverse proxy
./build set nginx yes
./build set nginx_proxy yes
./build nginx
# Rebuild Apache to work behind Nginx
./build apache
Configure Nginx Static File Caching
Add to /etc/nginx/conf.d/yourdomain.com.conf:
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.php$ {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Step 7: Enable Gzip Compression
# Apache: enable mod_deflate
# Add to /etc/httpd/conf.d/deflate.conf
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
AddOutputFilterByType DEFLATE application/javascript application/json
AddOutputFilterByType DEFLATE application/x-javascript text/javascript
DeflateCompressionLevel 6
</IfModule>
# Nginx: enable gzip
# Add to /etc/nginx/nginx.conf in http block
gzip on;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
gzip_min_length 1000;
Conclusion
DirectAdmin performance tuning follows a clear hierarchy: fix the PHP handler first (MPM Event + PHP-FPM), enable OPcache, then tune the database, then add a caching layer. Each step compounds: a properly tuned DirectAdmin server can handle 5–10x the concurrent users of a default installation with the same hardware. If you need performance tuning done correctly — with load testing before and after to confirm real-world improvement — the team at CloudHouse server management optimises DirectAdmin servers as part of our managed hosting service.
