The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and MySQL on OVH for WooCommerce
Nginx as a High-Performance Frontend for WooCommerce
When deploying WooCommerce on OVH, Nginx serves as the de facto standard for a high-performance web server and reverse proxy. Its event-driven architecture excels at handling concurrent connections, making it ideal for the often-spiky traffic patterns of e-commerce sites. We’ll focus on tuning Nginx for static asset delivery, request buffering, and efficient SSL/TLS termination.
Optimizing Static File Serving
Efficiently serving static assets (images, CSS, JS) is crucial. Nginx’s `sendfile` directive and `open_file_cache` are key. Ensure `sendfile` is enabled to bypass user space and directly transfer data from the file descriptor to the socket. The `open_file_cache` directive caches file descriptors and metadata, reducing the overhead of repeated `stat()` calls.
Nginx Configuration Snippet
# In your http block or a dedicated conf file included in http
http {
# ... other http configurations ...
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off; # Important for security
# Open file cache configuration
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# Gzip compression for text-based assets
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 image/svg+xml;
# ... other http configurations ...
}
Request Buffering and Client Limits
To prevent resource exhaustion from slow or malicious clients, configure request buffering. This limits the amount of data Nginx will buffer from a client. For a PHP-based application like WooCommerce, it’s also vital to set appropriate client body size limits.
Nginx Configuration Snippet
# In your http or server block client_body_buffer_size 128k; client_max_body_size 50m; # Adjust based on expected media uploads client_header_buffer_size 1k; large_client_header_buffers 4 128k; # Buffering for proxying to Gunicorn/FPM proxy_buffering on; proxy_buffer_size 128k; proxy_buffers 8 128k; proxy_busy_buffers_size 256k; proxy_temp_file_write_size 256k;
SSL/TLS Tuning
Secure connections are non-negotiable. Optimize SSL/TLS by enabling HTTP/2, using modern cipher suites, and configuring session caching. OVH often provides managed SSL certificates, simplifying deployment.
Nginx Configuration Snippet
# In your server block for SSL listen 443 ssl http2; listen [::]:443 ssl http2; ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; # Path to your OVH managed cert ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; # Path to your OVH managed key 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 if enabling # OCSP Stapling ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; # Use OVH DNS or preferred resolvers resolver_timeout 5s;
Gunicorn/PHP-FPM: The Application Backend
For a PHP-based application like WooCommerce, you’ll typically use PHP-FPM. If you’re using a Python framework with WooCommerce integrations (less common but possible), Gunicorn would be the WSGI HTTP Server. We’ll cover PHP-FPM tuning, as it’s the standard for WordPress/WooCommerce.
PHP-FPM Process Management
PHP-FPM’s process manager is critical for performance and stability. The choice between `dynamic` and `static` (or `ondemand`) depends on your server’s resources and traffic patterns. For most WooCommerce sites, `dynamic` offers a good balance.
PHP-FPM Configuration (`php-fpm.conf` or pool configuration)**
; In your PHP-FPM pool configuration file (e.g., /etc/php/8.1/fpm/pool.d/www.conf) ; Choose one process manager: dynamic, static, or ondemand ; 'dynamic' is generally recommended for variable loads. ; 'static' is good for consistent high load but can waste resources. ; 'ondemand' is resource-efficient but can have higher initial latency. pm = dynamic ; For 'dynamic' pm: ; pm.max_children: The maximum number of children that can be started. ; Adjust based on available RAM. A common starting point is (Total RAM - OS/Nginx RAM) / Average PHP Process Size. pm.max_children = 100 ; pm.start_servers: Number of child processes to start when the FPM master process is started. pm.start_servers = 10 ; pm.min_spare_servers: Minimum number of "idle" (free) processes. pm.min_spare_servers = 5 ; pm.max_spare_servers: Maximum number of "idle" (free) processes. pm.max_spare_servers = 20 ; For 'static' pm: ; pm.max_children = 100 ; Fixed number of children ; For 'ondemand' pm: ; pm.max_children = 100 ; Max children allowed ; Process idle timeout. If a child process has been idle for this amount of time, it will be killed. ; This is useful for 'dynamic' and 'ondemand' to free up resources. pm.process_idle_timeout = 10s ; Maximum number of requests each child process should execute before respawning. ; This helps to prevent memory leaks. pm.max_requests = 500 ; Request termination timeout with signal after 0 seconds. ; Default value is 0 (disabled). request_terminate_timeout = 30s ; Slowlog - logs scripts that take too long to execute. ; slowlog = /var/log/php-fpm/slowlog ; request_slowlog_timeout = 10s ; Set user and group for the pool user = www-data group = www-data ; Listen on a Unix socket for better performance than TCP/IP ; listen = /run/php/php8.1-fpm.sock ; listen.owner = www-data ; listen.group = www-data ; listen.mode = 0660 ; Or listen on a TCP port if Nginx is on a different server ; listen = 127.0.0.1:9000 ; listen.owner = www-data ; listen.group = www-data ; listen.mode = 0660
PHP Configuration Tuning (`php.ini`)
Beyond FPM process management, core PHP settings significantly impact WooCommerce performance. Key directives include memory limits, execution times, and opcache settings.
PHP `php.ini` Snippet
; In your php.ini file (e.g., /etc/php/8.1/fpm/php.ini) ; Increase memory limit for complex operations (e.g., product imports, large queries) memory_limit = 512M ; Increase max execution time for long-running processes (e.g., cron jobs, imports) max_execution_time = 120 ; Increase max input vars for handling large POST requests (e.g., many form fields) ; WooCommerce often requires this to be higher. max_input_vars = 3000 ; Enable and configure OPcache for significant performance gains opcache.enable=1 opcache.enable_cli=1 ; Enable for CLI scripts too (e.g., WP-CLI) opcache.memory_consumption=128 ; MB opcache.interned_strings_buffer=16 ; MB 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 if you have a robust deployment process opcache.save_comments=1 opcache.enable_file_override=0 opcache.fast_shutdown=0 opcache.optimization_level=0xFFFFFFFF ; Full optimization ; Error reporting for debugging in development, 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
MySQL/MariaDB Tuning for WooCommerce Data Loads
The database is often the bottleneck for dynamic content generation in WooCommerce. Tuning MySQL/MariaDB focuses on query optimization, efficient caching, and connection management.
InnoDB Buffer Pool
The InnoDB buffer pool is the most critical setting for InnoDB performance. It caches data and indexes. Aim to set `innodb_buffer_pool_size` to 50-75% of your server’s total RAM on a dedicated database server.
MySQL Configuration (`my.cnf` or `mysqld.cnf`)
[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 skip-external-locking # InnoDB Settings default_storage_engine = InnoDB innodb_file_per_table = 1 ; Recommended for better management and performance innodb_flush_log_at_trx_commit = 1 ; For ACID compliance. Set to 2 for higher performance with slight risk on crash. innodb_flush_method = O_DIRECT ; Recommended for Linux with hardware RAID or SSDs # Buffer Pool - CRITICAL for performance. Adjust based on available RAM. # Example for a 16GB RAM server (adjust accordingly) innodb_buffer_pool_size = 10G ; 10 Gigabytes # Other InnoDB Tuning innodb_log_file_size = 512M ; Adjust based on workload, larger can improve write performance innodb_log_buffer_size = 16M innodb_io_capacity = 2000 ; Adjust based on disk I/O capabilities (e.g., 200 for HDDs, 2000+ for SSDs) innodb_io_capacity_max = 4000 ; Max value for IO capacity # 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 thread_cache_size = 16 table_open_cache = 2000 table_definition_cache = 1000 # Sort and Join Buffers sort_buffer_size = 1M join_buffer_size = 1M read_buffer_size = 1M read_rnd_buffer_size = 2M # Temporary Tables tmp_table_size = 64M max_heap_table_size = 64M # Logging (Adjust for production) # log_error = /var/log/mysql/error.log # general_log = 0 # general_log_file = /var/log/mysql/mysql.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
Query Optimization and Slow Query Log
Regularly analyze your slow query log to identify and optimize inefficient queries. WooCommerce and WordPress can generate complex queries, especially with many plugins or custom code. Use tools like `pt-query-digest` from Percona Toolkit for analysis.
Enabling and Analyzing Slow Query Log
Ensure `slow_query_log = 1` and `long_query_time` are set appropriately in your `my.cnf`. After enabling, monitor the log file (e.g., `/var/log/mysql/mysql-slow.log`).
# Install Percona Toolkit (example for Debian/Ubuntu) sudo apt-get update sudo apt-get install percona-toolkit # Analyze the slow query log sudo pt-query-digest /var/log/mysql/mysql-slow.log > /var/log/mysql/mysql-slow-report.txt # Review the report for common problematic queries less /var/log/mysql/mysql-slow-report.txt
OVH Specific Considerations
OVH’s infrastructure, particularly their Public Cloud instances, offers various storage options (e.g., SSD, NVMe). Ensure your MySQL data directory is on the fastest available storage. For managed database services (if used), consult OVH’s documentation for their specific tuning parameters and best practices.
Putting It All Together: Monitoring and Iteration
Performance tuning is an ongoing process. Implement robust monitoring for Nginx, PHP-FPM, and MySQL. Key metrics include:
- Nginx: Active connections, requests per second, error rates (4xx, 5xx), upstream response times.
- PHP-FPM: Process usage (active, idle, waiting), request duration, memory usage.
- MySQL: Query throughput, slow queries, connection usage, buffer pool hit rate, disk I/O.
Tools like Prometheus with Node Exporter, Grafana for visualization, and specialized exporters for Nginx, PHP-FPM, and MySQL are invaluable. Regularly review these metrics after making configuration changes and iterate on your tuning strategy based on real-world performance data.