The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and MySQL on Linode for Magento 2
Nginx Configuration for Magento 2 Performance
Optimizing Nginx is crucial for serving Magento 2 efficiently. We’ll focus on caching, worker processes, and request handling.
Worker Processes and Connections
The worker_processes directive should ideally be set to the number of CPU cores available. worker_connections dictates the maximum number of simultaneous connections a worker can handle. A common starting point is 1024, but this can be tuned based on load.
Edit your main Nginx configuration file (e.g., /etc/nginx/nginx.conf):
user www-data;
worker_processes auto; # Or set to the number of CPU cores
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024; # Adjust based on expected concurrent users
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;
# ... other http configurations
}
HTTP/2 and SSL/TLS Optimization
Enabling HTTP/2 significantly improves performance through multiplexing and header compression. Ensure your SSL/TLS configuration is robust and efficient.
http {
# ... other http configurations
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name your_domain.com;
ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
# Modern SSL/TLS configuration (e.g., from Mozilla SSL Config Generator)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
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;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off; # Consider security implications
# HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# ... Magento 2 specific location blocks
}
# ... other http configurations
}
Magento 2 Specific Nginx Configuration
Magento 2’s recommended Nginx configuration includes directives for static file serving, PHP-FPM proxying, and caching. Ensure these are correctly implemented in your site’s configuration file (e.g., /etc/nginx/sites-available/your_domain.com).
server {
# ... SSL/TLS configuration as above
root /var/www/html/magento2; # Adjust to your Magento installation path
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ ^/(media|downloadable|static)/ {
# Magento 2 static content and media files
expires 30d;
access_log off;
add_header Cache-Control "public";
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
# Static assets caching
expires 1y;
access_log off;
add_header Cache-Control "public";
try_files $uri =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# Adjust socket path based on your PHP-FPM configuration
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; # Example for PHP 8.1
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300; # Increase timeout for long-running PHP scripts
}
# Deny access to sensitive files
location ~ /\.ht {
deny all;
}
# Magento 2 specific security and performance headers
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "no-referrer-when-downgrade";
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()"; # Adjust as needed
# Magento 2 Varnish configuration (if used)
# location / {
# proxy_pass http://varnish_backend;
# 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;
# }
}
Gunicorn/PHP-FPM Tuning for Magento 2
The choice between Gunicorn (for Python-based applications, though less common for Magento 2 directly) and PHP-FPM (for PHP applications like Magento 2) dictates the tuning parameters. We’ll focus on PHP-FPM as it’s the standard for Magento 2.
PHP-FPM Configuration
PHP-FPM’s performance is heavily influenced by its process manager settings. The primary configuration file is typically located at /etc/php/[version]/fpm/php-fpm.conf and pool configurations at /etc/php/[version]/fpm/pool.d/www.conf.
Process Manager (`pm`) Settings
The most common process managers are static, dynamic, and ondemand. For Magento 2, dynamic or static are generally preferred for consistent performance.
pm = dynamic: PHP-FPM starts a few children initially and spawns more as needed, up topm.max_children. It also kills idle processes to save resources.pm = static: PHP-FPM pre-forks a fixed number of children (pm.max_children) which are always available. This can offer lower latency but consumes more memory.
Edit your pool configuration file (e.g., /etc/php/8.1/fpm/pool.d/www.conf):
; /etc/php/8.1/fpm/pool.d/www.conf [www] user = www-data group = www-data listen = /var/run/php/php8.1-fpm.sock ; Ensure this matches Nginx config listen.owner = www-data listen.group = www-data listen.mode = 0660 pm = dynamic ; or static pm.max_children = 150 ; Adjust based on server RAM and load pm.start_servers = 10 ; For dynamic, number of children to start pm.min_spare_servers = 5 ; For dynamic, minimum number of idle resp. servers pm.max_spare_servers = 20 ; For dynamic, maximum number of idle resp. servers pm.max_requests = 500 ; Number of requests each child process should execute before respawning. Helps prevent memory leaks. ; For static pm: ; pm.max_children = 150 ; Fixed number of children ; Other important settings request_terminate_timeout = 120s ; Timeout for individual PHP script execution ; rlimit_files = 1024 ; Max open files per process ; rlimit_core = 0 ; Core dump size ; For Magento 2, increasing memory limit is often necessary php_admin_value[memory_limit] = 512M php_admin_value[max_execution_time] = 300 php_admin_value[upload_max_filesize] = 64M php_admin_value[post_max_size] = 64M php_admin_value[session.gc_maxlifetime] = 14400 ; 4 hours php_admin_value[opcache.enable] = 1 php_admin_value[opcache.memory_consumption] = 128 php_admin_value[opcache.interned_strings_buffer] = 16 php_admin_value[opcache.max_accelerated_files] = 10000 php_admin_value[opcache.revalidate_freq] = 60 php_admin_value[opcache.validate_timestamps] = 1 ; Set to 0 in production for max performance, but requires cache clearing on code deploy php_admin_value[opcache.save_comments] = 1 php_admin_value[opcache.enable_cli] = 1
Tuning Strategy:
- Monitor RAM: Start with a conservative
pm.max_children(e.g., 50-100) and monitor server RAM usage. Increase gradually. A common rule of thumb is(Total RAM - OS/Other Services RAM) / Average PHP Process Memory Usage. - Test Load: Use tools like ApacheBench (
ab) or k6 to simulate user load and observe response times, error rates, and PHP-FPM process counts. - Adjust Spares: For
dynamic,min_spare_serversandmax_spare_servershelp maintain a pool of ready processes without over-provisioning. pm.max_requests: Setting this to a reasonable value (e.g., 500-1000) helps prevent memory leaks in long-running processes.
PHP Settings (php.ini)
Beyond PHP-FPM pool settings, global PHP settings in php.ini are critical. Ensure OPcache is enabled and configured appropriately. The path to php.ini can be found using php --ini or by checking your web server’s PHP info output.
; Example settings in /etc/php/8.1/fpm/php.ini or /etc/php/8.1/cli/php.ini ; OPcache Configuration opcache.enable=1 opcache.enable_cli=1 opcache.memory_consumption=128 ; MB opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 opcache.revalidate_freq=60 ; Check for file updates every 60 seconds opcache.validate_timestamps=1 ; Set to 0 in production for maximum performance, but requires manual cache clearing on deploy. opcache.save_comments=1 opcache.enable_file_override=0 ; Memory and Execution Limits memory_limit = 512M max_execution_time = 300 max_input_time = 300 upload_max_filesize = 64M post_max_filesize = 64M ; Session Settings session.gc_maxlifetime = 14400 ; 4 hours session.cookie_lifetime = 0 session.cookie_httponly = 1 session.cookie_secure = 1 ; If using HTTPS ; Error Reporting (adjust for production) error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT display_errors = Off log_errors = On error_log = /var/log/php/php-error.log ; Ensure this directory is writable by the web server user
MySQL Tuning for Magento 2
Magento 2 is a database-intensive application. Optimizing MySQL (or MariaDB) is paramount. We’ll focus on key parameters in my.cnf.
Key MySQL Configuration Parameters
The main configuration file is typically /etc/mysql/my.cnf or /etc/mysql/mysql.conf.d/mysqld.cnf. Always back up your configuration before making changes.
[mysqld] # General Settings user = mysql pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock port = 3306 basedir = /usr datadir = /var/lib/mysql tmpdir = /tmp lc_messages_dir = /usr/share/mysql lc_messages = en_US # InnoDB Settings (Crucial for Magento) innodb_buffer_pool_size = 2G ; Adjust based on available RAM. Typically 50-70% of total RAM if DB is on dedicated server. innodb_log_file_size = 512M ; Larger log files can improve write performance but increase recovery time. innodb_log_buffer_size = 16M innodb_flush_log_at_trx_commit = 1 ; For ACID compliance. Set to 2 for slightly better performance at minor risk. innodb_flush_method = O_DIRECT ; Recommended for performance on Linux. innodb_file_per_table = 1 ; Recommended for better management and performance. innodb_io_capacity = 2000 ; Adjust based on disk I/O capabilities (e.g., SSDs). innodb_io_capacity_max = 4000 ; For SSDs. # Query Cache (Deprecated in MySQL 5.7, removed in 8.0. Use application-level caching instead.) # query_cache_type = 0 # query_cache_size = 0 # Connection Settings max_connections = 200 ; Adjust based on application needs and server resources. max_user_connections = 190 ; Slightly less than max_connections. wait_timeout = 600 ; Increase to prevent idle connections from closing too quickly. interactive_timeout = 600 # Thread Settings thread_cache_size = 16 thread_handling = pool-of-threads ; Recommended for performance # Sort Buffer Settings sort_buffer_size = 1M read_buffer_size = 1M read_rnd_buffer_size = 4M join_buffer_size = 4M # Table Cache Settings table_open_cache = 2000 table_definition_cache = 1000 # Logging (Disable for production unless debugging) # general_log_file = /var/log/mysql/mysql.log # general_log = 0 # log_error = /var/log/mysql/error.log # slow_query_log = 1 # slow_query_log_file = /var/log/mysql/mysql-slow.log # long_query_time = 2 ; Log queries taking longer than 2 seconds # Character Set character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci # MyISAM Settings (Magento primarily uses InnoDB, but good to have defaults) key_buffer_size = 16M myisam_recover_options = OFFTuning Strategy and Monitoring
innodb_buffer_pool_size: This is the most critical setting. Allocate as much RAM as possible, leaving enough for the OS and other services. On a dedicated database server, 70-80% of RAM is common. Monitor buffer pool hit rate usingSHOW ENGINE INNODB STATUS;. Aim for > 99%.
innodb_log_file_size: Larger log files reduce disk I/O by flushing less frequently but increase recovery time after a crash. 512MB to 1GB is a common range for busy systems.
innodb_flush_log_at_trx_commit: Setting to1provides full ACID compliance but is slower. Setting to2means logs are written to the OS buffer but not necessarily flushed to disk immediately, offering a performance boost at the cost of losing the last second of transactions in an OS crash. For Magento,1is generally recommended for data integrity.Monitoring: Use tools like Percona Monitoring and Management (PMM), Prometheus with `mysqld_exporter`, or even simple `SHOW GLOBAL STATUS;` and `SHOW ENGINE INNODB STATUS;` to track key metrics like:
-- Check InnoDB Buffer Pool Hit Rate SHOW ENGINE INNODB STATUS\G -- Check for Slow Queries SHOW GLOBAL STATUS LIKE 'Slow_queries'; -- Check for Connections SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL STATUS LIKE 'Max_used_connections'; -- Check for InnoDB Row Operations SHOW GLOBAL STATUS LIKE 'Innodb_rows_read'; SHOW GLOBAL STATUS LIKE 'Innodb_rows_inserted'; SHOW GLOBAL STATUS LIKE 'Innodb_rows_updated'; SHOW GLOBAL STATUS LIKE 'Innodb_rows_deleted';Magento Specific Database Considerations
Magento's EAV (Entity-Attribute-Value) model can lead to complex queries and large tables. Ensure your database schema is optimized and consider using tools like
pt-query-digestto analyze slow queries.Regularly run
OPTIMIZE TABLEon heavily fragmented tables (especially after large data imports/exports or deletions), but be mindful of the locking implications.# Example using pt-online-schema-change for non-blocking table optimization # pt-online-schema-change --alter "ENGINE=InnoDB" D=magento_db,t=catalog_product_entityFor very high-traffic sites, consider database replication (master-slave) for read-heavy workloads, offloading read queries to replica instances.
Putting It All Together: Linode Deployment and Monitoring
On a Linode instance, ensure your firewall is configured correctly to allow traffic on ports 80 and 443. Use
ufwfor easy management.sudo ufw allow 'Nginx Full' sudo ufw allow OpenSSH sudo ufw enable sudo ufw status verboseService Management and Reloads
After making configuration changes, always test and reload the services gracefully.
# Test Nginx configuration sudo nginx -t # Reload Nginx sudo systemctl reload nginx # Restart PHP-FPM (if major pool changes) sudo systemctl restart php8.1-fpm # Adjust version as needed # Restart MySQL sudo systemctl restart mysqlMonitoring and Alerting
Implement robust monitoring. Linode's built-in monitoring provides basic metrics. For deeper insights, consider:
- Prometheus & Grafana: For time-series metrics collection and visualization. Use exporters for Nginx, MySQL, Node Exporter for system metrics, and potentially a PHP-FPM exporter.
- ELK Stack (Elasticsearch, Logstash, Kibana): For centralized log management and analysis.
- Application Performance Monitoring (APM) tools: Such as New Relic or Datadog, for in-depth application-level performance analysis.
Set up alerts for critical thresholds: high CPU/RAM usage, disk I/O saturation, high error rates (Nginx 5xx, PHP errors), and slow database queries.