The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and MongoDB on Linode for WordPress
Nginx as a High-Performance Frontend for WordPress
When deploying WordPress on a VPS like Linode, Nginx serves as an exceptional frontend, efficiently handling static assets, SSL termination, and acting as a reverse proxy to your PHP application server (Gunicorn/FPM). Proper Nginx tuning is paramount for low latency and high throughput.
Optimizing Nginx Worker Processes and Connections
The `worker_processes` directive should ideally be set to the number of CPU cores available on your server. This allows Nginx to utilize all available processing power for handling requests concurrently. The `worker_connections` directive dictates the maximum number of simultaneous connections a single worker process can handle. A common starting point is 1024, but this can be increased based on your server’s RAM and expected load.
Nginx Configuration Snippet
Edit your main Nginx configuration file, typically located at /etc/nginx/nginx.conf.
user www-data;
worker_processes auto; # Or set to the number of CPU cores, e.g., worker_processes 4;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 4096; # Increased from default 1024
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# SSL Settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m; # Adjust size as needed
ssl_session_timeout 10m;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
# Gzip Compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# Logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Include virtual host configurations
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Leveraging FastCGI Caching for WordPress
For WordPress, which is heavily database-driven, implementing FastCGI caching can dramatically reduce server load by serving cached HTML pages directly from Nginx, bypassing PHP and database queries for most requests. This is particularly effective for static content or pages that don’t change frequently.
Enabling FastCGI Caching
First, ensure the ngx_http_fastcgi_cache_module is compiled into your Nginx binary. Most modern Nginx installations include this. You’ll need to define a cache zone and then configure your WordPress site’s location block to utilize it.
Nginx Configuration for FastCGI Caching
Add the following directives to your /etc/nginx/nginx.conf or a dedicated configuration file in /etc/nginx/conf.d/:
http {
# ... other http directives ...
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=wp_cache:100m inactive=60m;
fastcgi_temp_path /var/tmp/nginx/fastcgi_temp; # Ensure this directory exists and is writable by www-data
# Create cache directory if it doesn't exist
# sudo mkdir -p /var/cache/nginx/wordpress
# sudo chown www-data:www-data /var/cache/nginx/wordpress
# sudo mkdir -p /var/tmp/nginx/fastcgi_temp
# sudo chown www-data:www-data /var/tmp/nginx/fastcgi_temp
# ... rest of http directives ...
}
Then, within your WordPress site’s server block (e.g., in /etc/nginx/sites-available/your-wordpress-site):
server {
# ... other server directives ...
set $skip_cache 0;
# POST requests, URLs with query parameters, or admin pages should not be cached
if ($request_method = POST) {
set $skip_cache 1;
}
if ($query_string != "") {
set $skip_cache 1;
}
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-login.php") {
set $skip_cache 1;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# Assuming you are using PHP-FPM
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; # Adjust PHP version and socket path as needed
# FastCGI Cache Directives
fastcgi_cache_bypass $skip_cache;
fastcgi_cache_valid 200 60m; # Cache successful responses for 60 minutes
fastcgi_cache_key "$scheme$request_method$host$request_uri";
add_header X-Cache-Status $upstream_cache_status; # Useful for debugging cache hits/misses
# Other fastcgi_params as needed
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param QUERY_STRING $args;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param SERVER_NAME $host;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param REDIRECT_STATUS 200;
}
# Serve static files directly
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|webp)$ {
expires 365d;
add_header Cache-Control "public, no-transform";
access_log off;
}
# Deny access to hidden files
location ~ /\. {
deny all;
}
}
Tuning PHP-FPM for WordPress
PHP-FPM (FastCGI Process Manager) is the de facto standard for running PHP applications like WordPress. Its performance is heavily influenced by its process management and resource allocation settings.
Understanding PHP-FPM Process Management
PHP-FPM offers three primary process management strategies:
- Static: A fixed number of child processes are spawned when the FPM master process starts and remain active. This offers the lowest latency but can be wasteful if traffic is inconsistent.
- Dynamic: The number of child processes varies based on traffic. It starts with a minimum number, spawns more as needed up to a maximum, and kills idle processes to save resources.
- On-demand: Processes are spawned only when a request arrives and are killed after a period of inactivity. This is the most resource-efficient but can introduce higher latency for the first request after a period of idleness.
For WordPress, especially on a VPS, dynamic or static (if you have consistent high traffic) are generally preferred. Dynamic offers a good balance.
PHP-FPM Configuration Snippet
Edit your PHP-FPM pool configuration file. This is typically found in /etc/php/[VERSION]/fpm/pool.d/www.conf. Replace [VERSION] with your installed PHP version (e.g., 8.1).
; /etc/php/8.1/fpm/pool.d/www.conf [www] user = www-data group = www-data listen = /var/run/php/php8.1-fpm.sock ; Matches Nginx configuration listen.owner = www-data listen.group = www-data listen.mode = 0660 ; Process Management - Dynamic is often a good balance pm = dynamic pm.max_children = 50 ; Adjust based on RAM and CPU. Start lower and increase. pm.min_spare_servers = 5 ; Minimum number of idle processes pm.max_spare_servers = 15 ; Maximum number of idle processes pm.start_servers = 2 ; Initial number of processes when FPM starts ; If using Static: ; pm = static ; pm.max_children = 20 ; Fixed number of processes ; If using On-demand: ; pm = ondemand ; pm.max_children = 50 ; pm.process_idle_timeout = 10s ; Kill idle processes after 10 seconds ; Request handling request_terminate_timeout = 60s ; Timeout for a single script execution ; request_slowlog_timeout = 10s ; Log scripts that take longer than this (useful for debugging) ; slowlog = /var/log/php/php8.1-fpm-slow.log ; Other settings ; php_admin_value[memory_limit] = 256M ; Adjust as needed for WordPress plugins ; php_admin_value[upload_max_filesize] = 64M ; php_admin_value[post_max_size] = 64M ; php_admin_value[max_execution_time] = 120
After modifying the PHP-FPM configuration, restart the service:
sudo systemctl restart php8.1-fpm # Adjust version as needed
MongoDB Tuning for WordPress (if applicable)
While WordPress traditionally uses MySQL/MariaDB, some plugins or custom solutions might leverage MongoDB. If you are using MongoDB, tuning its performance is crucial.
Key MongoDB Configuration Parameters
The primary configuration file for MongoDB is typically /etc/mongod.conf.
Storage Engine and WiredTiger
MongoDB 3.2+ defaults to the WiredTiger storage engine, which is generally performant. Ensure it’s enabled and consider its cache size.
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
engine: wiredTiger
wiredTiger:
engineConfig:
cacheSizeGB: 0.75 # Allocate 75% of available RAM to WiredTiger cache, or a fixed value like 4GB. Adjust based on server RAM.
collectionConfig:
BlockCompressor: snappy # Or zstd for better compression if CPU is not a bottleneck
indexConfig:
prefixCompression: true
Network and Performance Settings
Adjusting network and query performance settings can also yield benefits.
# ... other settings ... net: port: 27017 bindIp: 127.0.0.1, [your_server_private_ip] # Bind to localhost and private IP for security # Profiling (enable for debugging, disable in production for performance) # systemLog: # verbosity: 0 # quiet: false # path: /var/log/mongodb/mongod.log # logAppend: true # Operation Profiling (use with caution in production) # operationProfiling: # mode: "off" # "off", "slowOp", "all" # slowOpThresholdMs: 100 # Query Analysis (for slow query logging) # slowQueryLogFile: /var/log/mongodb/mongod-slow.log # slowQueryThresholdMs: 200 # Log queries slower than 200ms
After modifying mongod.conf, restart MongoDB:
sudo systemctl restart mongod
Monitoring and Iterative Tuning
Performance tuning is an ongoing process. Regularly monitor your server’s resource utilization (CPU, RAM, I/O) and application response times. Tools like htop, iotop, Nginx’s stub_status module, PHP-FPM’s status page, and MongoDB’s diagnostic commands are invaluable.
Key Monitoring Metrics
- Nginx: Active connections, requests per second, error rates, cache hit/miss ratio (via
X-Cache-Statusheader). - PHP-FPM: Number of active processes, idle processes, request duration.
- MongoDB: Query execution times, cache hit rates, disk I/O, network traffic.
- System: CPU load, memory usage, swap usage, disk I/O wait times.
Use these metrics to identify bottlenecks and iteratively adjust configuration parameters. Remember to test changes in a staging environment before applying them to production.