• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 9+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Scaling WooCommerce on OVH to Handle 50,000+ Concurrent Requests

Scaling WooCommerce on OVH to Handle 50,000+ Concurrent Requests

OVH Infrastructure Blueprint for High-Traffic WooCommerce

Achieving 50,000+ concurrent requests for a WooCommerce store on OVH requires a multi-layered approach, moving beyond single-server optimizations. This blueprint details the architectural components and configurations necessary to build a robust, scalable, and performant e-commerce platform. We’ll focus on leveraging OVH’s dedicated server and managed services, emphasizing statelessness, asynchronous processing, and intelligent caching.

Database Layer: PostgreSQL & Redis for Session/Cache

The database is often the primary bottleneck. For high-traffic WooCommerce, we’ll offload session management and transient caching to Redis and utilize a robust PostgreSQL instance, potentially with read replicas.

PostgreSQL Configuration for WooCommerce

Tuning PostgreSQL is critical. Focus on memory allocation, connection pooling, and query optimization. We’ll assume a dedicated PostgreSQL server or a managed OVHcloud SQL Private Cloud instance.

`postgresql.conf` Tuning

Key parameters to adjust in postgresql.conf:

  • shared_buffers: Typically 25% of system RAM. For a 64GB RAM server, 16GB is a good starting point.
  • work_mem: Crucial for sorting and hashing. Start with 16MB and monitor pg_stat_statements for high-cost sorts. Increase cautiously.
  • maintenance_work_mem: For VACUUM, CREATE INDEX, etc. Set to 128MB or higher.
  • effective_cache_size: Set to 50-75% of total RAM, reflecting OS and PostgreSQL’s own caches.
  • max_connections: This needs careful consideration. Too high can exhaust memory. Use a connection pooler (like PgBouncer) on the application side instead of relying solely on this. Set it to a reasonable number for direct connections (e.g., 100-200) and let the pooler handle the rest.
  • wal_buffers: 16MB is often sufficient.
  • checkpoint_completion_target: 0.9.
  • random_page_cost: Lower this if using SSDs (e.g., 1.1).
  • effective_io_concurrency: Set to the number of concurrent I/O threads your storage can handle (e.g., 2-4 for typical SSDs).

Example snippet (adjust values based on your server specs):

shared_buffers = 16GB
work_mem = 32MB
maintenance_work_mem = 256MB
effective_cache_size = 48GB
max_connections = 150
wal_buffers = 16MB
checkpoint_completion_target = 0.9
random_page_cost = 1.1
effective_io_concurrency = 4

Connection Pooling with PgBouncer

Directly managing thousands of PostgreSQL connections from a web server farm is inefficient and can overwhelm the database. PgBouncer acts as a lightweight connection pooler.

PgBouncer Installation and Configuration

Install PgBouncer (e.g., sudo apt-get install pgbouncer on Debian/Ubuntu).

`pgbouncer.ini`

Key settings in pgbouncer.ini:

  • pool_mode = session: Recommended for WooCommerce to handle transactions correctly.
  • max_client_conn: Number of connections from your web servers. Start with a few hundred per web server.
  • default_pool_size: Number of connections to the PostgreSQL server per database. This should be less than max_connections in PostgreSQL.
  • reserve_pool_size: A small pool for urgent connections.
[databases]
mydb = host=your_pg_host port=5432 dbname=your_db_name user=your_user password=your_password

[pgbouncer]
listen_port = 6432
listen_addr = 0.0.0.0
auth_file = /etc/pgbouncer/userlist.txt
admin_users = pgbouncer_admin
stats_users = stats_user
pool_mode = session
max_client_conn = 2000
default_pool_size = 50
reserve_pool_size = 10
log_connections = 0
log_disconnections = 0
log_pooler_updates = 0
log_statements = 0
`userlist.txt`
"pgbouncer_admin" "md5_hash_of_admin_password"
"stats_user" "md5_hash_of_stats_password"
"your_user" "md5_hash_of_your_user_password"

Ensure your PHP (or other application language) database connection string points to the PgBouncer port (e.g., pgsql:host=your_app_server_ip;port=6432;dbname=your_db_name).

Redis for Sessions and Transients

Offloading WordPress/WooCommerce sessions and transient data to Redis dramatically reduces database load and improves response times.

Redis Configuration

On your Redis server, ensure sufficient memory is allocated and persistence is configured appropriately for your needs (e.g., RDB snapshots or AOF logging). For session storage, persistence might be less critical than for caching.

# redis.conf
maxmemory 8gb # Adjust based on available RAM
maxmemory-policy allkeys-lru # Evict least recently used keys when maxmemory is reached
appendonly yes # Enable AOF for durability if needed

WooCommerce/WordPress Integration

Use a robust plugin like “Redis Object Cache” or “WP Redis” to integrate Redis with WordPress. For WooCommerce sessions, ensure the plugin or a custom solution correctly redirects session handling.

Example PHP Snippet for Session Handling (Conceptual)

This is a simplified illustration. A plugin would handle this more comprehensively.

if ( class_exists( 'Redis' ) ) {
    // Attempt to connect to Redis for sessions
    $redis = new Redis();
    try {
        $redis->connect('your_redis_host', 6379);
        // Set session handler to Redis
        session_set_save_handler(
            array($redis, 'open'),
            array($redis, 'close'),
            array($redis, 'read'),
            array($redis, 'write'),
            array($redis, 'destroy'),
            array($redis, 'gc')
        );
        // Set a timeout for sessions
        ini_set('session.gc_maxlifetime', 86400); // 24 hours
        session_start();
    } catch (RedisException $e) {
        // Fallback to file-based sessions or log error
        error_log("Redis connection failed for sessions: " . $e->getMessage());
        session_start(); // Fallback
    }
} else {
    // Fallback to default file-based sessions
    session_start();
}

// For transients, the object cache plugin handles this automatically.

Web Server Layer: Nginx & PHP-FPM

A highly optimized Nginx and PHP-FPM setup is crucial for serving dynamic content efficiently. We’ll employ multiple PHP-FPM pools and aggressive caching strategies.

Nginx Configuration for WooCommerce

Nginx will handle static file serving, SSL termination, request routing, and reverse proxying to PHP-FPM. Load balancing across multiple web servers is essential.

`nginx.conf` (Main Configuration)

user www-data;
worker_processes auto; # Set to number of CPU cores
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 4096; # Adjust based on system limits and expected load
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off; # Hide Nginx version

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # SSL Settings
    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; # Adjust size as needed
    ssl_session_timeout 10m;

    # 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;

    # Logging
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;

    # Include virtual host configurations
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

WooCommerce Site Configuration (`/etc/nginx/sites-available/woocommerce`)

# Define upstream PHP-FPM pools
upstream php-fpm-pool {
    # Use least_conn for load balancing if multiple FPM servers are used
    # least_conn;

    # Define sockets or IPs for your PHP-FPM workers
    # Example using Unix sockets (preferred for performance on same machine)
    server unix:/var/run/php/php8.1-fpm-site1.sock;
    server unix:/var/run/php/php8.1-fpm-site2.sock;
    # Example using TCP/IP (if FPM is on different servers)
    # server 192.168.1.10:9000;
    # server 192.168.1.11:9000;
}

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri; # Redirect HTTP to HTTPS
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;

    # SSL Certificates
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf; # Recommended SSL options

    # Root directory and index files
    root /var/www/yourdomain.com/public_html;
    index index.php index.html index.htm;

    # Caching for static assets (adjust expiry times)
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|webp|woff|woff2|ttf|eot)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # Deny access to sensitive files
    location ~ /\.ht {
        deny all;
    }

    # Serve static files directly
    location ~ ^/(wp-content/uploads|images|css|js|static)/ {
        try_files $uri $uri/ =404;
    }

    # PHP processing
    location ~ \.php$ {
        try_files $uri =404;
        include snippets/fastcgi-php.conf;
        # Pass requests to the upstream PHP-FPM pool
        fastcgi_pass php-fpm-pool;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        # FastCGI buffer settings (tune based on request sizes)
        fastcgi_buffers 8 16k;
        fastcgi_buffer_size 32k;
        fastcgi_read_timeout 300; # Increase timeout for long-running scripts
    }

    # WordPress permalinks
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    # Security 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 "strict-origin-when-cross-origin";
    # add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none';"; # Example CSP, requires careful tuning

    # Rate Limiting (example for login attempts)
    location ~* ^/wp-login\.php$ {
        limit_req zone=myloginburst burst=5 nodelay;
        try_files $uri =404;
        include snippets/fastcgi-php.conf;
        fastcgi_pass php-fpm-pool;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

# Rate limiting zone definition (add to nginx.conf or a separate conf file)
# http {
#     # ... other http settings ...
#     limit_req_zone $binary_remote_addr zone=myloginburst:10m rate=5r/m; # 5 requests per minute per IP
#     # ... rest of http settings ...
# }

PHP-FPM Configuration

Running multiple PHP-FPM pools allows for better resource isolation and tuning. Each pool can be configured with different process management settings.

Multiple PHP-FPM Pools (`/etc/php/8.1/fpm/pool.d/www.conf`, `site1.conf`, `site2.conf`)

Create separate pool configuration files (e.g., /etc/php/8.1/fpm/pool.d/site1.conf, /etc/php/8.1/fpm/pool.d/site2.conf) and adjust the listen directive to match the Nginx upstream configuration.

; /etc/php/8.1/fpm/pool.d/site1.conf
[site1]
user = www-data
group = www-data
listen = /var/run/php/php8.1-fpm-site1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 50      ; Max number of child processes
pm.start_servers = 5      ; Number of servers started on startup
pm.min_spare_servers = 10 ; Min number of spare servers
pm.max_spare_servers = 20 ; Max number of spare servers
pm.max_requests = 500     ; Max requests per child process before respawning

request_terminate_timeout = 120s ; Timeout for script execution
process_control_timeout = 20s

; /etc/php/8.1/fpm/pool.d/site2.conf (similar, potentially with different pm settings for different workloads)
[site2]
user = www-data
group = www-data
listen = /var/run/php/php8.1-fpm-site2.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = ondemand
pm.max_children = 100
pm.process_idle_timeout = 10s

request_terminate_timeout = 180s

Key PHP-FPM Tuning Parameters:

  • pm: Choose between static, dynamic, or ondemand. dynamic is often a good balance. ondemand can save resources but has higher latency for initial requests.
  • pm.max_children: The absolute maximum number of processes that will be spawned. This is the most critical setting. Calculate based on available RAM per process.
  • pm.start_servers, pm.min_spare_servers, pm.max_spare_servers: For dynamic PM, these control how the pool scales.
  • pm.max_requests: Prevents memory leaks by respawning processes after a certain number of requests.
  • request_terminate_timeout: Crucial for preventing long-running requests from hogging FPM processes.

Caching Strategies

A multi-tiered caching strategy is non-negotiable for handling high concurrency.

Page Caching (Nginx FastCGI Cache)

Nginx’s built-in FastCGI cache can serve full HTML pages directly without hitting PHP-FPM for many requests, especially for product listings, category pages, and even logged-out user views of product pages.

Nginx Configuration for FastCGI Cache

# Add to http block in nginx.conf
fastcgi_cache_path /var/cache/nginx/woocommerce levels=1:2 keys_zone=wc_cache:100m inactive=60m max_size=10g;
fastcgi_temp_path /var/tmp/nginx/fcgi_temp;

# In your server block:
location ~ \.php$ {
    # ... existing fastcgi_pass and fastcgi_param directives ...

    fastcgi_cache wc_cache;
    fastcgi_cache_valid 200 302 10m; # Cache successful responses for 10 minutes
    fastcgi_cache_valid 404 1m;      # Cache 404s for 1 minute
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
    fastcgi_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
    add_header X-Cache-Status $upstream_cache_status; # Useful for debugging

    # Bypass cache for logged-in users or specific POST requests
    fastcgi_cache_bypass $http_cookie;
    fastcgi_cache_bypass $request_method;
    proxy_no_cache $http_cookie;
    proxy_no_cache $request_method;

    # Ignore cache for AJAX requests or specific query parameters
    if ($http_x_requested_with = "XMLHttpRequest") {
        fastcgi_cache_bypass 1;
        proxy_no_cache 1;
    }
    if ($args ~* "wc-ajax=get_refreshed_fragments") {
        fastcgi_cache_bypass 1;
        proxy_no_cache 1;
    }
}

Ensure the cache directories exist and have correct permissions: sudo mkdir -p /var/cache/nginx/woocommerce /var/tmp/nginx/fcgi_temp and sudo chown www-data:www-data /var/cache/nginx/woocommerce /var/tmp/nginx/fcgi_temp.

Object Caching (Redis)

As mentioned, Redis is used for object caching (WordPress transients, options, post data). This is handled by plugins like “Redis Object Cache”.

Browser Caching

Configured in Nginx (see expires directive in the Nginx site configuration above) to cache static assets like images, CSS, and JavaScript in the user’s browser.

Content Delivery Network (CDN)

Offload static assets (images, CSS, JS) to a CDN to reduce load on your OVH servers and improve global delivery speed.

Asynchronous Processing & Background Jobs

Tasks like order processing emails, image resizing, and complex calculations should not block the main web request. Use a robust queueing system.

RabbitMQ or Redis Queue

For high-volume sites, RabbitMQ is a powerful message broker. For simpler needs, Redis can also serve as a queue backend.

Example: WooCommerce Order Processing Hook (Conceptual)

add_action( 'woocommerce_order_status_changed', 'enqueue_order_processing_job', 10, 3 );

function enqueue_order_processing_job( $order_id, $old_status, $order ) {
    // Only process for specific status transitions, e.g., 'processing'
    if ( 'processing' === $order->get_status() && 'processing' !== $old_status ) {
        // Assume a queueing library/service is configured (e.g., using Predis for Redis or AMQP for RabbitMQ)
        $queue = QueueService::getInstance(); // Your custom queue service
        $queue->push( 'process_order_email', array( 'order_id' => $order_id ) );
        $queue->push( 'update_inventory_after_order', array( 'order_id' => $order_id ) );
    }
}

// A separate worker script would consume these jobs:
// Example worker logic (simplified)
/*
class OrderProcessorWorker {
    public function process_order_email( $data ) {
        $order_id = $data['order_id'];
        // Send order confirmation email logic...
        error_log("Processed order email for ID: " . $order_id);
    }

    public function update_inventory_after_order( $data ) {
        $order_id = $data['order_id'];
        // Update inventory logic...
        error_log("Updated inventory for order ID: " . $order_id);
    }
}
*/

You would then run dedicated worker processes (e.g., using Supervisor) that continuously poll the queue and execute these tasks.

Monitoring and Alerting

Implement comprehensive monitoring for all components: Nginx, PHP-FPM, PostgreSQL, Redis, and application-level metrics.

  • Nginx: Status (active connections, requests/sec), error rates, response times.
  • PHP-FPM: Pool status (active processes, idle processes), request duration, slow requests.
  • PostgreSQL: Query performance (pg_stat_statements), connection counts, replication lag, disk I/O.
  • Redis: Memory usage, hit rate, latency, connected clients.
  • Application: WooCommerce-specific metrics (orders per minute, checkout abandonment rate), error logs.

Tools like Prometheus with Grafana, Datadog, or New Relic are essential for visibility and proactive issue detection.

OVH Specific Considerations

Leverage OVH’s network infrastructure. Ensure your dedicated servers are in close proximity to your managed database instances (if applicable) to minimize latency. Utilize OVH’s load balancers if managing multiple web servers.

Conclusion

Scaling WooCommerce to 50,000+ concurrent requests is an architectural challenge. It requires a deep understanding of each layer, from the database to the web server and application code. By implementing robust database tuning, connection pooling, multi-layered caching, asynchronous processing, and diligent monitoring, you can build a highly performant and scalable e-commerce platform on OVH.

Primary Sidebar

A little about the Author

Having 9+ Years of Experience in Software Development.
Expertised in Php Development, WordPress Custom Theme Development (From scratch using underscores or Genesis Framework or using any blank theme or Premium Theme), Custom Plugin Development. Hands on Experience on 3rd Party Php Extension like Chilkat, nSoftware.

Recent Posts

  • Step-by-Step: Diagnosing thread pools deadlock during concurrent ActiveRecord transaction processing on Linode Servers
  • Securing Your E-commerce APIs: Preventing SQL Injection (SQLi) in customized checkout queries in WooCommerce Implementations
  • Disaster Recovery 101: Architecting Auto-Failovers for MySQL and Ruby Deployments on Linode
  • High-Throughput Caching Strategies: Scaling MySQL for Perl Application APIs
  • Disaster Recovery 101: Architecting Auto-Failovers for DynamoDB and Laravel Deployments on DigitalOcean

Copyright © 2026 · Vinay Vengala