• 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 » The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and Redis on Linode for WooCommerce

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and Redis on Linode for WooCommerce

Nginx as a High-Performance Frontend Proxy

For a WooCommerce site, Nginx serves as the critical entry point, handling static assets, SSL termination, and proxying dynamic requests to the application server. Optimizing Nginx is paramount for low latency and high throughput. We’ll focus on key directives for connection handling, caching, and request buffering.

Nginx Configuration Tuning

The primary configuration file is typically located at /etc/nginx/nginx.conf. We’ll adjust the http block for global settings and then define specific server blocks for our WooCommerce application.

Global HTTP Settings

Within the http block, consider the following directives:

  • worker_processes auto;: Dynamically scales worker processes to match the number of CPU cores, maximizing parallel request handling.
  • worker_connections 4096;: Sets the maximum number of simultaneous connections a worker process can handle. This should be set high enough to accommodate peak traffic, considering the OS limits (ulimit -n).
  • multi_accept on;: Allows workers to accept multiple new connections at once.
  • keepalive_timeout 65;: Defines the timeout for keep-alive connections. A moderate value prevents idle connections from consuming resources indefinitely.
  • sendfile on;: Enables efficient file transfer by copying data directly from the kernel’s page cache to the socket, bypassing user space.
  • tcp_nopush on;: Instructs Nginx to send file headers in one packet, reducing network round trips.
  • tcp_nodelay on;: Disables the Nagle algorithm, which can improve latency for small packets.
  • types_hash_max_size 2048;: Increases the hash table size for MIME types to prevent potential lookups from becoming a bottleneck under heavy load.
  • server_names_hash_bucket_size 128;: Similar to types_hash_max_size, this tunes the hash table for server names.

A sample http block:

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

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_names_hash_bucket_size 128;

    # Gzip compression for dynamic content
    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;

    # Buffering for upstream requests
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;
    proxy_busy_buffers_size 256k;

    worker_processes auto;
    worker_connections 4096;
    multi_accept on;

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

Server Block for WooCommerce

This block defines how Nginx handles requests for your WooCommerce domain. Key optimizations include static file caching, SSL configuration, and proxying to the application server (Gunicorn/FPM).

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name your_domain.com www.your_domain.com;

    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
    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

    # HSTS Header
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # Static File Caching
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|webp|woff|woff2|ttf|eot)$ {
        expires 365d;
        add_header Cache-Control "public, immutable";
        access_log off;
        log_not_found off;
    }

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

    # Proxy to Application Server (e.g., Gunicorn)
    location / {
        proxy_pass http://unix:/run/gunicorn.sock; # Or http://127.0.0.1:8000;
        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 potentially long requests
        proxy_connect_timeout 75s;
    }

    # Optional: Serve static files directly if not using a CDN
    # location /static/ {
    #     alias /path/to/your/wordpress/static/;
    #     expires 365d;
    #     add_header Cache-Control "public, immutable";
    # }

    # Optional: PHP-FPM configuration if using PHP backend
    # location ~ \.php$ {
    #     include snippets/fastcgi-php.conf;
    #     fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; # Adjust version
    #     fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    #     include fastcgi_params;
    # }

    # Access and error logs
    access_log /var/log/nginx/your_domain.com.access.log;
    error_log /var/log/nginx/your_domain.com.error.log;
}

Gunicorn/PHP-FPM Tuning for Application Performance

The choice between Gunicorn (for Python-based applications like Django/Flask, often used with WooCommerce plugins or custom backends) and PHP-FPM (for traditional PHP WooCommerce installations) dictates the tuning strategy. Both aim to efficiently manage worker processes to handle incoming requests.

Gunicorn Tuning (Python Applications)

Gunicorn is a Python WSGI HTTP Server. Its performance is heavily influenced by the number of worker processes and threads.

  • --workers [N]: The recommended number of worker processes is typically (2 * CPU_CORES) + 1. This formula balances CPU utilization and prevents context-switching overhead.
  • --threads [N]: For I/O-bound applications, increasing threads per worker can improve concurrency. Start with 2-4 threads per worker and monitor.
  • --worker-class [class]: For I/O-bound workloads, gevent or eventlet can offer better concurrency than the default sync worker class.
  • --bind [address]: Bind to a Unix socket (e.g., unix:/run/gunicorn.sock) for performance gains when Nginx is on the same server, or to 127.0.0.1:[port].
  • --timeout [seconds]: Set a reasonable timeout to prevent workers from hanging indefinitely, but not so low that it interrupts legitimate long-running requests.

Example Gunicorn startup command:

gunicorn --workers 5 --threads 2 --worker-class gevent --bind unix:/run/gunicorn.sock --timeout 120 your_project.wsgi:application

Ensure the user running Gunicorn has permissions to access the socket file, and that Nginx is configured to proxy to it.

PHP-FPM Tuning (PHP Applications)

PHP-FPM (FastCGI Process Manager) manages PHP worker pools. The configuration is typically found in /etc/php/[version]/fpm/pool.d/www.conf.

  • pm = dynamic or ondemand: dynamic allows FPM to scale workers based on load. ondemand starts new workers only when needed, saving resources but potentially increasing initial request latency. For busy sites, dynamic is often preferred.
  • pm.max_children: The maximum number of child processes that will be spawned. This is a critical setting. Calculate based on available RAM: (Total RAM - RAM for OS/Nginx/Redis) / Average RAM per PHP process.
  • pm.start_servers: The number of child processes to start when PHP-FPM starts.
  • pm.min_spare_servers: The minimum number of idle (spare) processes.
  • pm.max_spare_servers: The maximum number of idle (spare) processes.
  • request_terminate_timeout: Similar to Gunicorn’s timeout, prevents runaway scripts.
  • pm.process_idle_timeout: If using ondemand, this defines how long an idle process waits before being killed.

Example www.conf snippet for a Linode with 4GB RAM:

; /etc/php/8.1/fpm/pool.d/www.conf

[www]
user = www-data
group = www-data
listen = /var/run/php/php8.1-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 100       ; Adjust based on RAM. ~100MB per process is a rough estimate.
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.process_idle_timeout = 10s
pm.max_requests = 500       ; Restart workers after X requests to prevent memory leaks

request_terminate_timeout = 120s
request_slowlog_timeout = 30s
slowlog = /var/log/php/php8.1-fpm.slow.log

catch_workers_output = yes
; rlimit_files = 1024
; rlimit_nofile = 65536
;

After modifying PHP-FPM configuration, restart the service: sudo systemctl restart php8.1-fpm.

Redis for Object Caching and Session Management

Redis is indispensable for WooCommerce performance, primarily for object caching (reducing database load) and session management (faster user experience). Proper configuration of Redis itself, and how your application interacts with it, is key.

Redis Server Configuration

The main configuration file is /etc/redis/redis.conf.

  • maxmemory <bytes>: Crucial for preventing Redis from consuming all available RAM. Set this to a value less than your total system RAM, leaving room for the OS, Nginx, and your application. For example, on a 4GB Linode, maxmemory 2gb is a reasonable starting point.
  • maxmemory-policy allkeys-lru: Defines how Redis evicts keys when maxmemory is reached. allkeys-lru (Least Recently Used) is a common and effective policy for caching.
  • save "": Disable RDB persistence if you are using AOF or if persistence is not critical (e.g., for cache-only scenarios). This can improve performance by avoiding disk I/O. If persistence is needed, configure AOF.
  • appendonly yes: Enable Append Only File (AOF) persistence for better durability than RDB snapshots.
  • tcp-backlog 511: Increase the TCP backlog queue size to handle a higher number of incoming connections gracefully.
  • tcp-keepalive 300: Set TCP keepalive to detect and close stale connections.

Example redis.conf snippet:

# /etc/redis/redis.conf

# Memory Management
maxmemory 2gb
maxmemory-policy allkeys-lru

# Persistence (Choose AOF or RDB, or disable if not needed)
# save "" # Disable RDB if using AOF or no persistence needed
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec # A good balance between performance and durability

# Network and Connections
tcp-backlog 511
tcp-keepalive 300
bind 127.0.0.1 ::1 # Bind to localhost if Nginx/App are on the same server
# If Redis is on a separate server, adjust bind and protected-mode accordingly.

# Logging
loglevel notice
logfile /var/log/redis/redis-server.log

Restart Redis after changes: sudo systemctl restart redis-server.

WooCommerce Redis Integration

Ensure your WooCommerce installation (or its underlying framework) is configured to use Redis for object caching and sessions. This typically involves a plugin or custom configuration.

For PHP-based WooCommerce, a plugin like “Redis Object Cache” or “W3 Total Cache” (with Redis enabled) is common. For Python backends, you’d configure your framework’s caching and session backends.

// Example using a hypothetical PHP Redis caching class/plugin
// This would typically be in wp-config.php or a plugin's bootstrap file

define('WP_REDIS_CLIENT', 'phpredis'); // Or 'pecl_redis'
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_PASSWORD', null); // If you set a password in redis.conf
define('WP_REDIS_DATABASE', 0); // Use database 0 for object cache

// For Session Management (if supported by plugin/framework)
// define('WP_REDIS_SESSION_ENABLE', true);
// define('WP_REDIS_SESSION_DATABASE', 1); // Use a different DB for sessions

Verify Redis is being used by checking your application logs or using Redis monitoring tools (e.g., redis-cli INFO memory, redis-cli INFO stats).

Monitoring and Diagnostics

Continuous monitoring is essential to validate these tuning efforts and identify new bottlenecks. Key metrics include:

  • Nginx: Request rate, error rates (4xx, 5xx), connection counts, latency. Use stub_status module.
  • Gunicorn/PHP-FPM: Worker utilization, request queue length, response times, memory usage per worker.
  • Redis: Memory usage, hit rate (redis-cli INFO stats), latency, connected clients.
  • System: CPU load, memory usage, disk I/O, network traffic.

Tools like htop, iotop, netdata, Prometheus/Grafana, or cloud provider monitoring services are invaluable.

Nginx Stub Status

Enable the stub_status module in your Nginx server block to get real-time connection metrics:

location /nginx_status {
    stub_status;
    allow 127.0.0.1; # Restrict access
    deny all;
}

Access http://your_domain.com/nginx_status (from localhost or allowed IP) to see output like:

Active connections: 1234
server accepts handled requests requests per second
 1234567 1234567 1234567890 1234.56
Reading: 10 Writing: 5 Waiting: 1099

High Waiting connections might indicate upstream bottlenecks or insufficient worker connections. High Reading/Writing can point to slow clients or network issues.

Redis Monitoring

Use redis-cli for quick checks:

redis-cli
127.0.0.1:6379> INFO memory
used_memory:123456789
used_memory_human:117.75M
maxmemory:2147483648
maxmemory_human:2.00G
maxmemory_policy:allkeys-lru

127.0.0.1:6379> INFO stats
total_commands_processed:1234567890
instantaneous_ops_per_sec:12345
keyspace_hits:123456789
keyspace_misses:12345678
-------------------------
db0:keys=100000,expires=0
db1:keys=50000,expires=0

127.0.0.1:6379> CLIENT LIST

A low hit rate (keyspace_hits / (keyspace_hits + keyspace_misses)) indicates caching isn’t effective or Redis is too small/evicting too aggressively. High memory usage approaching maxmemory requires tuning or scaling.

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