The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and DynamoDB on Google Cloud for Magento 2
Nginx Configuration for Magento 2 on Google Cloud
Optimizing Nginx is paramount for serving Magento 2 efficiently, especially in a cloud environment like Google Cloud. We’ll focus on key directives that impact performance and security, assuming a typical setup with Gunicorn (for PHP-FPM) or direct PHP-FPM.
Core Nginx Performance Tuning
The nginx.conf file, typically located at /etc/nginx/nginx.conf or within /etc/nginx/conf.d/, is the starting point. We’ll adjust worker processes and connections.
Worker Processes: Set worker_processes to the number of CPU cores available. On Google Cloud Compute Engine instances, this is easily discoverable.
worker_processes auto; # Or set to the number of CPU cores
Worker Connections: This directive defines the maximum number of simultaneous connections that each worker process can handle. A common starting point is 1024, but this can be increased based on expected load. Ensure the system’s file descriptor limit is also increased accordingly.
events {
worker_connections 4096; # Adjust based on load and system limits
multi_accept on;
}
File Descriptors: To support higher worker_connections, increase the system’s open file descriptor limit. This is typically done in /etc/security/limits.conf or via systemd service files for Gunicorn/PHP-FPM.
# Example for limits.conf * soft nofile 65536 * hard nofile 65536 # For systemd services, add to the service file: # LimitNOFILE=65536
Magento 2 Specific Nginx Directives
Magento 2 benefits from specific Nginx configurations for static file serving, caching, and security headers. This is usually configured within the site’s specific server block, often in /etc/nginx/sites-available/your-magento-site.
Static File Optimization
Leverage Nginx’s ability to serve static assets directly and efficiently. Set appropriate cache headers and use expires directives.
location ~ ^/(media|static)/ {
expires 30d; # Cache static assets for 30 days
add_header Cache-Control "public";
# For Magento 2.4+, static content is symlinked. Ensure correct permissions.
# If using Varnish, this might be handled differently.
try_files $uri $uri/ /pub/static.php?$args;
}
Important Note: For Magento 2.4 and later, static content is typically symlinked to pub/static. Ensure the Nginx user has read permissions to these symlinks and the underlying files. If you encounter 404s for static assets, this is often the culprit.
PHP-FPM Integration (Gunicorn/PHP-FPM)
When using Nginx as a reverse proxy to PHP-FPM (either directly or via Gunicorn), configure the fastcgi_pass directive correctly. Ensure your PHP-FPM pool is configured for performance.
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# Assuming PHP-FPM is running on a Unix socket
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; # Adjust PHP version and path
# Or if using TCP/IP
# fastcgi_pass 127.0.0.1:9000;
}
Gunicorn Configuration for PHP-FPM
If you’re using Gunicorn as a process manager for PHP-FPM, its configuration will influence how PHP requests are handled. A common setup involves Gunicorn listening on a socket and Nginx proxying to it.
# gunicorn.conf.py (example) bind = "unix:/path/to/your/gunicorn.sock" workers = 4 # Adjust based on CPU cores threads = 2 # Adjust based on workload timeout = 120 # Increase for long-running Magento tasks accesslog = "/var/log/gunicorn/access.log" errorlog = "/var/log/gunicorn/error.log" loglevel = "info" # If Gunicorn is proxying to PHP-FPM directly (less common for Magento) # This would involve a custom worker class or a WSGI app that interfaces with PHP-FPM. # More typically, Gunicorn would serve static assets and proxy dynamic requests to PHP-FPM.
In a typical Magento setup with Gunicorn, Gunicorn often acts as the WSGI server for a Python application that might handle some routing or API calls, and then proxies dynamic PHP requests to PHP-FPM. Nginx then proxies to Gunicorn.
# Nginx config proxying to Gunicorn
location / {
proxy_pass http://unix:/path/to/your/gunicorn.sock;
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;
proxy_read_timeout 300s; # Increase timeout for Magento
proxy_connect_timeout 75s;
}
PHP-FPM Tuning
The PHP-FPM configuration (php-fpm.conf and pool configurations in pool.d/www.conf) is critical. For Magento, which can have high memory and process demands, tuning is essential.
Process Management
Choose between static, dynamic, or ondemand process management. For Magento, dynamic or ondemand are often preferred to conserve resources when idle, but static can offer more consistent performance under heavy load if resources are plentiful.
; pm = dynamic ; pm.max_children = 50 ; pm.start_servers = 5 ; pm.min_spare_servers = 2 ; pm.max_spare_servers = 10 ; pm.process_idle_timeout = 10s ; OR pm = ondemand ; pm.max_children = 100 ; pm.process_idle_timeout = 10s ; OR pm = static ; pm.max_children = 100
Tuning Parameters:
pm.max_children: The maximum number of child processes that will be spawned. This is the most critical setting. Set it based on available RAM and expected concurrent requests. A common formula is(Total RAM - RAM for OS/Nginx) / Average PHP-FPM process size.pm.start_servers: Number of child processes started when PHP-FPM starts.pm.min_spare_servers: Minimum number of idle supervisor processes.pm.max_spare_servers: Maximum number of idle supervisor processes.pm.process_idle_timeout: The number of seconds after which an idle process will be killed.
Memory Limits and Execution Time
Magento is notoriously resource-intensive. Increase memory_limit and max_execution_time for CLI commands and potentially for web requests.
memory_limit = 1024M ; Or higher, depending on your instance size and workload max_execution_time = 300 ; For CLI commands, this might need to be even higher max_input_vars = 3000 ; Often needed for large forms or configurations
These settings are typically found in /etc/php/X.Y/fpm/php.ini. Remember to restart PHP-FPM after making changes: sudo systemctl restart phpX.Y-fpm.
DynamoDB Tuning for Magento 2 Caching
Using DynamoDB as a caching backend for Magento 2 (e.g., for session storage, page cache, or configuration cache) requires careful consideration of provisioned throughput and item structure.
Provisioned Throughput (RCU/WCU)
DynamoDB is a provisioned throughput database. You pay for the Read Capacity Units (RCUs) and Write Capacity Units (WCUs) you provision. For Magento, cache reads and writes can be frequent.
Estimating Needs:
- RCUs: Each RCU allows one strongly consistent read per second, or two eventually consistent reads per second. Magento cache reads are typically eventually consistent.
- WCUs: Each WCU allows one write per second.
Monitor your DynamoDB table’s consumed throughput using CloudWatch metrics. Start with a conservative provision and scale up based on observed traffic. Use Auto Scaling for DynamoDB to automatically adjust provisioned throughput based on demand.
# Example CloudWatch metric to monitor: ConsumedReadCapacityUnits # Example CloudWatch metric to monitor: ConsumedWriteCapacityUnits
Table Design and Item Structure
The primary key design is crucial for efficient access patterns and avoiding hot partitions. For Magento caching, a common pattern is:
{
"TableName": "magento2-cache",
"KeySchema": [
{ "AttributeName": "cache_key", "KeyType": "HASH" } // Partition Key
],
"AttributeDefinitions": [
{ "AttributeName": "cache_key", "AttributeType": "S" }
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 100, // Start here and adjust
"WriteCapacityUnits": 50 // Start here and adjust
}
}
Cache Key Strategy: Ensure your cache keys are sufficiently unique and distributed. Magento’s cache system generates keys based on the cache type and identifier. Avoid very long or highly skewed keys that could lead to hot partitions.
TTL (Time To Live) for Cache Items
DynamoDB doesn’t have a native TTL feature like Redis. You need to implement this logic in your application or use DynamoDB Streams with a Lambda function to expire old items. A common approach is to store an expiration timestamp within the item and periodically clean up expired items.
// Example PHP code to store an item with expiration
$tableName = 'magento2-cache';
$cacheKey = 'my_magento_cache_id';
$cacheValue = serialize(['data' => 'some_value']);
$ttlSeconds = 3600; // 1 hour
$expirationTimestamp = time() + $ttlSeconds;
$client->putItem([
'TableName' => $tableName,
'Item' => [
'cache_key' => ['S' => $cacheKey],
'cache_value' => ['S' => $cacheValue], // Store serialized data
'expires_at' => ['N' => (string)$expirationTimestamp], // Store expiration timestamp
],
]);
// Example PHP code to retrieve an item and check expiration
$result = $client->getItem([
'TableName' => $tableName,
'Key' => [
'cache_key' => ['S' => $cacheKey],
],
]);
if ($result['Item']) {
$currentItem = $result['Item'];
$currentTimestamp = time();
if (isset($currentItem['expires_at']) && $currentItem['expires_at']['N'] > $currentTimestamp) {
// Cache is valid
$cachedData = unserialize($currentItem['cache_value']['S']);
// Use $cachedData
} else {
// Cache expired, fetch fresh data
}
} else {
// Cache not found, fetch fresh data
}
For automated cleanup, consider a Lambda function triggered on a schedule (e.g., every hour) that scans for items where expires_at is in the past and deletes them. This scan operation will consume RCUs, so factor that into your provisioned throughput.
Monitoring and Iteration
Continuous monitoring is key. Utilize Google Cloud’s Stackdriver (now Operations Suite) for Nginx logs, PHP-FPM logs, and DynamoDB CloudWatch metrics. Set up alerts for high error rates, slow response times, and approaching provisioned throughput limits in DynamoDB. Regularly review these metrics and iterate on your configurations.