Scaling Magento 2 on OVH to Handle 50,000+ Concurrent Requests
OVH Infrastructure Deep Dive for High-Traffic Magento 2
Achieving 50,000+ concurrent requests on a Magento 2 platform hosted on OVH requires a meticulously engineered infrastructure. This isn’t about simply throwing more CPU at the problem; it’s about strategic resource allocation, optimized software configurations, and robust caching layers. We’ll focus on a typical OVH Public Cloud setup, leveraging their High-Performance Compute (HPC) instances and managed services where appropriate.
Database Layer: PostgreSQL Optimization for Magento 2
Magento 2’s reliance on database operations is substantial. For high concurrency, a single MySQL instance, even a powerful one, becomes a bottleneck. We’ll explore a PostgreSQL-based approach, known for its superior concurrency handling and advanced indexing capabilities, deployed on OVH’s Managed Databases for PostgreSQL. This offloads management overhead and provides a highly available, scalable solution.
Key PostgreSQL Configuration Parameters:
shared_buffers: Crucial for caching data blocks. A common starting point is 25% of available RAM, but tuning based on workload is essential. For a 64GB RAM instance, this could be 16GB.effective_cache_size: Informs the query planner about available OS and disk cache. Set to 50-75% of total RAM.work_mem: Memory for internal sort operations and hash tables. Needs careful tuning; too high can lead to OOM errors, too low degrades sort performance. Start with 16MB and monitor.maintenance_work_mem: For VACUUM, ANALYZE, etc. Set higher thanwork_mem, e.g., 256MB.max_connections: Must be sufficient for all application servers and background workers. Magento can be connection-hungry. Start with 200 and monitor.wal_buffers: For WAL (Write-Ahead Logging) data. 16MB is a good starting point.checkpoint_segments(ormax_wal_sizein newer versions): Controls how often checkpoints occur. A larger value reduces I/O but increases recovery time. For high write loads,max_wal_sizeof 2GB or more might be appropriate.
Example postgresql.conf Snippet (for a 64GB RAM instance):
# Memory shared_buffers = 16GB effective_cache_size = 48GB work_mem = 32MB maintenance_work_mem = 512MB # Connections max_connections = 300 # WAL settings wal_buffers = 16MB max_wal_size = 4GB checkpoint_completion_target = 0.9 # Logging (adjust as needed for debugging) log_destination = 'stderr' logging_collector = on log_directory = 'pg_log' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' log_statement = 'none' # 'ddl', 'mod', 'all' for debugging log_min_duration_statement = 1000 # Log statements longer than 1s
Index Optimization: Magento 2 heavily uses EAV. Ensure proper indexing strategies are in place, especially for frequently queried attributes. Regularly analyze slow queries using pg_stat_statements and create appropriate indexes. Consider partial indexes for specific use cases.
Example Index Creation:
-- Example: Indexing product collection by status and visibility for faster category page loads CREATE INDEX idx_catalog_product_entity_status_visibility ON catalog_product_entity (status, visibility); -- Example: Indexing for specific attribute lookups if EAV is a bottleneck -- This is highly dependent on your specific attributes and query patterns -- CREATE INDEX idx_catalog_product_entity_varchar_attribute_id_value -- ON catalog_product_entity_varchar (attribute_id, value);
Web Server Layer: Nginx and PHP-FPM Tuning
For serving static assets and proxying dynamic requests, Nginx is the de facto standard. PHP-FPM handles the PHP execution. Both require aggressive tuning for high concurrency.
Nginx Configuration:
worker_processes: Set to the number of CPU cores available.worker_connections: The maximum number of simultaneous connections that can be opened by a worker process. A common value is 4096 or higher.keepalive_timeout: Shorten to reduce idle connections, e.g., 30 seconds.gzip: Enable compression for text-based assets.open_file_cache: Cache file descriptors and metadata.client_max_body_size: Adjust based on expected upload sizes.
Example nginx.conf Snippet:
user www-data;
worker_processes auto; # Or set to number of CPU cores
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 8192; # Increased from default
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30; # Reduced from default
types_hash_max_size 2048;
server_tokens off; # Hide Nginx version
# 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 image/svg+xml;
# File descriptor and open file cache
worker_rlimit_nofile 65536;
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# Client body size
client_max_body_size 100M; # Adjust as needed
include /etc/nginx/mime.types;
default_type application/octet-stream;
# ... other configurations ...
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
PHP-FPM Configuration:
pm: Set todynamicorondemandfor better resource utilization.dynamicis generally preferred for high traffic.pm.max_children: The most critical setting. This determines how many PHP processes can run concurrently. Calculate based on available RAM per process. If each PHP process consumes 50MB, and you have 64GB RAM, you might aim forpm.max_children = 1000(64 * 1024 / 50 ≈ 1300, but leave room for OS and other services).pm.start_servers,pm.min_spare_servers,pm.max_spare_servers: Tune these for dynamic PM to manage process pool efficiently.max_execution_time: Set to a reasonable value (e.g., 180 seconds) for long-running tasks, but be mindful of potential abuse.memory_limit: Increase significantly for Magento, e.g., 512MB or 1GB.opcache.enable,opcache.memory_consumption,opcache.interned_strings_buffer,opcache.revalidate_freq: Essential for performance. Enable OPcache and tune its memory.
Example php-fpm.d/www.conf Snippet:
[www] user = www-data group = www-data listen = /run/php/php7.4-fpm.sock # Adjust PHP version as needed listen.owner = www-data listen.group = www-data listen.mode = 0660 pm = dynamic pm.max_children = 1200 pm.start_servers = 100 pm.min_spare_servers = 50 pm.max_spare_servers = 200 pm.process_idle_timeout = 10s pm.max_requests = 500 # Restart processes after N requests to prevent memory leaks request_terminate_timeout = 180 # seconds request_slowlog_timeout = 30 # seconds, for slowlog file ; PHP Settings memory_limit = 1024M upload_max_filesize = 100M post_max_size = 100M max_input_vars = 3000 ; OPcache Settings opcache.enable = 1 opcache.memory_consumption = 256 opcache.interned_strings_buffer = 16 opcache.max_accelerated_files = 10000 opcache.revalidate_freq = 2 # seconds, set to 0 for production if not using file monitoring opcache.validate_timestamps = 1 # Set to 0 for production if using deployment scripts to clear cache opcache.enable_cli = 1
Caching Strategy: Varnish, Redis, and Magento’s Built-in Cache
A multi-layered caching strategy is non-negotiable. We’ll employ Varnish for full-page caching, Redis for session storage and object caching, and ensure Magento’s internal cache types are optimized.
Varnish Cache Configuration:
Varnish sits in front of Nginx, serving cached pages directly without hitting the application or database for most requests. This is the primary driver for handling high concurrent *read* traffic.
# VCL Configuration (varnishd -f /etc/varnish/default.vcl)
# This is a simplified example. Production VCL needs careful handling of cookies,
# AJAX requests, and specific Magento URL patterns.
vcl 4.1;
backend default {
.host = "127.0.0.1"; # IP of your Nginx server
.port = "80";
.connect_timeout = 60s;
.first_byte_timeout = 600s;
.between_bytes_timeout = 600s;
}
sub vcl_recv {
# Remove cookies for anonymous users to enable caching
if (req.http.Cookie) {
set req.http.Cookie = regsuball(req.http.Cookie, "(^|; )" + req.http.Cookie + "(;|$)", "");
if (req.http.Cookie ~ "(?i)PHPSESSID=") {
return (pass); # Pass if session cookie is present
}
}
# Normalize URL
set req.url = purl;
# Ignore requests for specific file types that shouldn't be cached
if (req.url ~ "\.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|eot|svg)(\?.*)?$") {
return (pass);
}
# Allow POST requests to pass through
if (req.method == "POST") {
return (pass);
}
# Remove query strings for static assets if possible (advanced)
# if (req.url ~ "(/media/js/|/static/frontend/)") {
# set req.url = regsub(req.url, "\?.*$", "");
# }
return (hash);
}
sub vcl_backend_response {
# Set cache headers
set beresp.grace = 1h; # Allow serving stale content for 1 hour if backend is down
# Cache static assets for a long time
if (beresp.url ~ "\.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|eot|svg)(\?.*)?$") {
set beresp.ttl = 1y;
set beresp.uncacheable = false;
}
# Cache HTML pages for a shorter duration
if (beresp.content_type ~ "text/html") {
set beresp.ttl = 1h; # Adjust based on your cache invalidation strategy
set beresp.uncacheable = false;
}
# Don't cache error pages
if (beresp.status >= 400) {
set beresp.uncacheable = true;
set beresp.ttl = 0s;
}
# Magento specific: Handle ESI (Edge Side Includes) if used
# if (beresp.http.X-Magento-ESI == "1") {
# set beresp.uncacheable = true;
# }
return (deliver);
}
sub vcl_deliver {
# Add cache status header for debugging
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
return (deliver);
}
Redis for Session and Cache:
Offload session handling and Magento’s object cache to Redis. This significantly reduces database load and improves response times.
// app/etc/env.php configuration for Redis
[
'front' => [
'type' => 'Magento\\Framework\\Cache\\Backend\\Redis',
'options' => [
'server' => '10.0.0.1', // IP of your Redis instance
'port' => 6379,
'database' => 0, // Or a dedicated DB for cache
'password' => '', // If Redis is password protected
'compress_data' => '1', // Enable compression
'compression_library' => 'gzip', // or 'lzf', 'lz4'
]
],
'default' => [
'type' => 'Magento\\Framework\\Cache\\Backend\\Redis',
'options' => [
'server' => '10.0.0.1',
'port' => 6379,
'database' => 1, // Dedicated DB for default cache
'password' => '',
'compress_data' => '1',
'compression_library' => 'gzip',
]
],
// Add other cache types as needed, e.g., page_cache, block_html
'page_cache' => [
'type' => 'Magento\\Framework\\Cache\\Backend\\Redis',
'options' => [
'server' => '10.0.0.1',
'port' => 6379,
'database' => 2, // Dedicated DB for page cache
'password' => '',
'compress_data' => '1',
'compression_library' => 'gzip',
]
]
],
'session' => [
'save' => 'redis',
'redis' => [
'host' => '10.0.0.1',
'port' => 6379,
'password' => '',
'timeout' => '2.5',
'persistent_identifier' => '',
'database' => 3, // Dedicated DB for sessions
'compression_threshold' => '2048',
'compression_library' => 'gzip',
'log_level' => '3', // Adjust as needed
'compress_data' => '1',
]
],
// ... other configurations
];
Magento Cache Management:
Ensure all relevant Magento cache types are enabled and flushed appropriately during deployments. Use the command line for efficiency:
# Enable all cache types php bin/magento cache:enable # Flush specific cache types php bin/magento cache:flush block_html config full_page_cache # Flush all cache types php bin/magento cache:flush
Application Layer: PHP and Magento Optimizations
Beyond PHP-FPM settings, application-level tuning is crucial.
- Composer Optimizations: Use
--optimize-autoloaderand--no-devflags during deployments. - Static Content Deployment: Pre-generate static content for all themes and locales.
- Theme Optimization: Minimize custom themes and third-party extensions. Audit and remove unused code.
- JavaScript Bundling and Minification: Magento’s built-in tools can help, but consider advanced techniques or third-party solutions for aggressive bundling.
- Database Queries: Profile and optimize slow queries within Magento modules. Use tools like Blackfire.io or Xdebug for deep profiling.
- Cron Jobs: Schedule cron jobs during off-peak hours and ensure they are efficient. Consider using a distributed cron system if necessary.
- Background Processing: Offload non-critical tasks (e.g., email sending, image processing) to message queues (e.g., RabbitMQ) and background workers.
Example: Optimizing Autoloader
# During deployment composer install --no-dev --optimize-autoloader
Example: Static Content Deployment
# For all themes and locales (adjust as needed) php bin/magento setup:static-content:deploy en_US en_GB fr_FR --theme Magento/luma --theme Vendor/mytheme
Infrastructure Scaling on OVH Public Cloud
OVH Public Cloud offers several components that are vital for scaling:
- High-Performance Compute (HPC) Instances: Utilize instances with high CPU clock speeds and ample RAM (e.g., the
GRA-XXXorBHS-XXXseries). - Load Balancers: Use OVH’s Load Balancer service to distribute traffic across multiple web server instances. Configure health checks to automatically remove unhealthy nodes.
- Managed Databases: As discussed, leverage Managed Databases for PostgreSQL and Redis to ensure high availability and scalability without the operational burden.
- Object Storage (S3-compatible): Store media assets in Object Storage and serve them via a CDN for faster global delivery and reduced load on web servers.
- Auto-Scaling Groups: While not a native feature of OVH Public Cloud in the same way as AWS, you can script auto-scaling using their API and orchestration tools (like Kubernetes or custom scripts) to add/remove web server instances based on metrics like CPU utilization or request queue length.
Load Balancer Configuration Example (Conceptual):
# OVH Load Balancer Configuration (via API or Control Panel)
# Frontend Configuration
Frontend: HTTP (Port 80)
- Protocol: TCP
- Default Backend Pool: WebServers
# Backend Pool: WebServers
- Algorithm: Round Robin or Least Connections
- Health Check:
- Protocol: HTTP
- Port: 80
- URI: /healthcheck.php (a simple PHP script returning 200 OK)
- Interval: 10s
- Timeout: 5s
- Fall/Rise: 3/3
# Backend Servers:
- Server 1: IP_of_webserver_1 (Port 80)
- Server 2: IP_of_webserver_2 (Port 80)
- ...
- Server N: IP_of_webserver_N (Port 80)
# Frontend Configuration
Frontend: HTTPS (Port 443)
- Protocol: TCP
- SSL Termination: Enabled (using OVH SSL certificates)
- Default Backend Pool: WebServers
Monitoring and Alerting
Continuous monitoring is key to maintaining performance and identifying issues before they impact users. Implement robust monitoring for:
- Server Metrics: CPU, RAM, Disk I/O, Network Traffic (using tools like Prometheus/Grafana, Datadog, or OVH’s built-in monitoring).
- Application Performance: Request latency, error rates, throughput (APM tools like New Relic, Dynatrace, or Blackfire.io).
- Database Performance: Query times, connection counts, cache hit rates (e.g.,
pg_stat_statements, Redis monitoring). - Varnish Cache Hit Rate: Crucial for understanding caching effectiveness.
- Queue Lengths: For message queues if used.
Set up alerts for critical thresholds (e.g., high CPU, low disk space, high error rates, low Varnish hit rate) to proactively address potential problems.