The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and PostgreSQL on OVH for Magento 2
Nginx Configuration Tuning for Magento 2
Optimizing Nginx is paramount for serving high-traffic Magento 2 instances. We’ll focus on key directives that directly impact performance and resource utilization, particularly within the context of an OVH VPS or dedicated server environment where direct control over the webserver is expected.
Worker Processes and Connections
The worker_processes directive determines how many worker processes Nginx will spawn. A common recommendation is to set this to the number of CPU cores available. For worker_connections, this defines the maximum number of simultaneous connections that each worker process can handle. The total number of connections is worker_processes * worker_connections. Ensure your system’s file descriptor limits are high enough to accommodate this.
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;
events {
worker_connections 1024; # Adjust based on expected load and system limits
multi_accept on;
}
After modifying nginx.conf, test the configuration and reload Nginx:
sudo nginx -t sudo systemctl reload nginx
Keepalive Timeout and Buffers
keepalive_timeout controls how long an idle HTTP connection will remain open. A lower value can free up resources faster, but too low can increase overhead for clients making frequent requests. client_body_buffer_size and client_header_buffer_size are crucial for handling large requests. Magento 2 can generate substantial POST requests, especially during checkout.
http {
# ... other http directives ...
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65; # Default is 65, consider reducing slightly if needed
keepalive_requests 1000; # Maximum requests per keep-alive connection
client_body_buffer_size 128k; # Default is 8k, increase for large POSTs
client_header_buffer_size 128k; # Default is 1k, increase for large headers
large_client_header_buffers 4 128k; # For very large headers
# ... other http directives ...
}
Gzip Compression
Enabling Gzip compression significantly reduces the size of transferred data, leading to faster page load times. Ensure it’s configured appropriately for static assets and dynamic content.
http {
# ...
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6; # Compression level (1-9)
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
gzip_min_length 1000; # Minimum response length to compress
# ...
}
Caching Strategies
Leverage Nginx’s ability to cache static assets. This offloads requests from your application server and database.
location ~* ^/(media|static)/ {
expires 30d; # Cache for 30 days
add_header Cache-Control "public, immutable";
access_log off;
log_not_found off;
}
For dynamic content, consider using Nginx’s FastCGI cache or proxy_cache if you’re not relying solely on Varnish or Redis for page caching. This is a more advanced topic and requires careful configuration to avoid cache invalidation issues with Magento 2.
Gunicorn/PHP-FPM Tuning
The choice between Gunicorn (for Python-based applications, though Magento 2 is PHP) and PHP-FPM depends on your specific setup. Assuming a standard PHP deployment for Magento 2, we’ll focus on PHP-FPM.
PHP-FPM Process Manager Settings
PHP-FPM’s process manager controls how worker processes are spawned and managed. The pm setting can be static, dynamic, or ondemand. For production, dynamic or static are generally preferred.
Edit your PHP-FPM pool configuration file, typically found in /etc/php/[version]/fpm/pool.d/www.conf (replace [version] with your PHP version, e.g., 8.1).
; Choose one of: static, dynamic, ondemand pm = dynamic ; If dynamic, these settings are used: pm.max_children = 50 ; Max number of children serving requests. Adjust based on RAM. pm.start_servers = 5 ; Number of children created at start. pm.min_spare_servers = 5 ; Number of children when idle. pm.max_spare_servers = 10 ; Number of children when idle. pm.max_requests = 500 ; Max requests per child process before respawning. ; If static, these settings are used: ; pm = static ; pm.max_children = 50 ; Fixed number of children. ; If ondemand, these settings are used: ; pm = ondemand ; pm.max_children = 50 ; pm.min_spare_servers = 1 ; pm.max_spare_servers = 3 ; pm.process_idle_timeout = 10s ; pm.max_requests = 0
Tuning Considerations:
pm.max_children: This is the most critical setting. Too high, and you’ll run out of RAM. Too low, and requests will queue up. A good starting point is to monitor your server’s RAM usage under load and set this to a value that keeps RAM usage below 80-90%. Each PHP-FPM worker can consume 20-50MB of RAM or more, depending on Magento’s complexity and loaded extensions.pm.max_requests: Setting this to a reasonable number helps prevent memory leaks from accumulating over time.
After changes, test and reload PHP-FPM:
sudo systemctl restart php[version]-fpm
PHP.ini Optimizations
Several PHP.ini settings directly impact Magento 2 performance.
; In your php.ini file (e.g., /etc/php/[version]/fpm/php.ini) memory_limit = 512M ; Increase for Magento's memory needs max_execution_time = 180 ; Allow longer execution for complex tasks upload_max_filesize = 64M ; Adjust as needed for media uploads post_max_size = 64M ; Must be >= upload_max_filesize max_input_vars = 3000 ; Crucial for Magento's form processing opcache.enable = 1 opcache.memory_consumption = 128 ; MB, adjust based on your code size 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 max performance, but requires manual cache clearing on deploy. opcache.enable_cli = 1
Remember to restart PHP-FPM after modifying php.ini.
PostgreSQL Tuning for Magento 2
PostgreSQL is the default database for Magento 2. Optimizing its configuration is critical for database-intensive operations common in e-commerce.
Shared Buffers and WAL Settings
shared_buffers is the most important parameter, determining how much memory PostgreSQL uses for caching data. A common recommendation is 25% of your total system RAM, but this can be adjusted based on your server’s RAM and workload.
Write-Ahead Logging (WAL) settings are crucial for data integrity and performance. Tuning wal_buffers and checkpoint_segments (or max_wal_size in newer versions) can improve write performance.
Edit your postgresql.conf file (location varies by OS and version, e.g., /etc/postgresql/[version]/main/postgresql.conf).
# Example for a server with 16GB RAM shared_buffers = 4GB ; 25% of 16GB effective_cache_size = 12GB ; ~75% of RAM, helps query planner # WAL settings wal_buffers = 16MB ; Default is 3MB, increase for busy systems wal_writer_delay = 200ms ; Default is 200ms, can be tuned max_wal_size = 4GB ; For PostgreSQL 9.5+, replaces checkpoint_segments checkpoint_timeout = 15min ; For PostgreSQL 9.5+, default is 5min checkpoint_completion_target = 0.9 ; Default is 0.5, helps spread I/O # Other important settings work_mem = 32MB ; Memory for internal sort operations and hash tables maintenance_work_mem = 256MB ; Memory for VACUUM, CREATE INDEX, etc. random_page_cost = 1.1 ; Default is 4.0, tune for SSDs seq_page_cost = 1.0 ; Default is 1.0 effective_io_concurrency = 200 ; For SSDs, tune based on IOPS default_statistics_target = 100 ; For better query planning
After modifying postgresql.conf, restart the PostgreSQL service:
sudo systemctl restart postgresql
Connection Pooling
Magento 2 can open many database connections. Using a connection pooler like PgBouncer can significantly reduce overhead and improve performance by managing a pool of persistent connections to PostgreSQL.
Install PgBouncer (e.g., sudo apt install pgbouncer) and configure its pgbouncer.ini file (typically in /etc/pgbouncer/).
[databases] pgbouncer = host=127.0.0.1 port=5432 dbname=your_magento_db [pgbouncer] ; Connection pooling mode: session, transaction, or statement ; 'transaction' is generally recommended for Magento 2 pool_mode = transaction ; Maximum number of clients that can connect to pgbouncer max_client_conn = 1000 ; Maximum number of server connections per database default_pool_size = 20 ; Minimum number of server connections per database min_pool_size = 5 ; Maximum number of server connections per database when pool_mode is 'transaction' ; This is crucial for Magento 2. A common recommendation is 10-20 per worker process. ; If you have 50 PHP-FPM workers, and each can potentially open a connection, ; you might set this to 500 or more, depending on your database server's capacity. ; For a default setup with 50 max_children, start with default_pool_size = 50, max_db_connections = 100 max_db_connections = 500 ; Authentication method (e.g., md5, scram-sha-256) auth_type = md5 auth_file = /etc/pgbouncer/userlist.txt ; Log settings logfile = /var/log/pgbouncer/pgbouncer.log pidfile = /var/run/pgbouncer/pgbouncer.pid
Create the userlist.txt file with your database credentials:
"your_magento_db" "your_db_user" "md5your_db_password_hash"
Start and enable PgBouncer:
sudo systemctl start pgbouncer sudo systemctl enable pgbouncer
Ensure your Magento 2 app/etc/env.php is configured to connect to PgBouncer instead of directly to PostgreSQL:
return [
// ... other config ...
'db' => [
'connection' => [
'host' => '127.0.0.1', // Connect to PgBouncer
'dbname' => 'your_magento_db',
'username' => 'your_db_user',
'password' => 'your_db_password',
'model' => 'mysqlImproved', // Or appropriate model
'port' => 6432, // PgBouncer's default port
'initStatements' => 'SET NAMES utf8;',
'driver_options' => [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
]
],
// ... other db config ...
],
// ... other config ...
];
Monitoring and Iteration
Performance tuning is an iterative process. Continuously monitor your server’s resource utilization (CPU, RAM, I/O, network) using tools like htop, vmstat, iostat, and PostgreSQL’s pg_stat_activity. Use Magento’s built-in profiling tools and external APM solutions to identify bottlenecks. Adjust these configurations based on observed performance metrics and load testing. Remember to clear Magento’s cache (bin/magento cache:clean and bin/magento cache:flush) after significant configuration changes.