• 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 AWS for WooCommerce

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

Nginx Configuration for High-Traffic WooCommerce

Optimizing Nginx is paramount for serving static assets, handling SSL termination, and acting as a reverse proxy to your application servers. For a WooCommerce site, this means efficient caching, robust connection management, and intelligent request routing.

Static Asset Caching and Compression

Leverage browser caching for static assets like images, CSS, and JavaScript. This significantly reduces load times for repeat visitors. Enable Gzip or Brotli compression to minimize transfer sizes.

Example Nginx Configuration Snippet

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

# Browser caching for static assets
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;
}

# Cache static assets for a long time
location ~* \.(css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# Cache images for a long time
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

Reverse Proxy and Worker Processes

Configure Nginx as a reverse proxy to your Gunicorn (for Python/Django/Flask) or PHP-FPM (for PHP/WordPress) backend. Tune the number of worker processes and connections based on your server’s CPU cores and expected load.

Nginx `nginx.conf` Tuning

# Adjust worker_processes based on CPU cores. A common starting point is 'auto' or the number of cores.
worker_processes auto;
# Or, for a 4-core CPU:
# worker_processes 4;

# Increase the maximum number of open file descriptors
worker_rlimit_nofile 65535;

events {
    # Use epoll for Linux, kqueue for BSD/macOS
    use epoll;
    # Max connections per worker process. Should be high enough to handle concurrent requests.
    worker_connections 4096;
    # Multiplier for worker_connections if using accept_mutex
    # multi_accept on;
}

http {
    # ... other http configurations ...

    # Keepalive connections to upstream servers
    keepalive_timeout 65;
    keepalive_requests 1000;

    # Proxy settings
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
    proxy_buffer_size 16k;
    proxy_buffers 4 32k;
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;

    # Enable HTTP/2 for improved performance
    http2_max_concurrent_streams 100;
    http2_push_preload on;

    # ... server blocks ...
}

SSL/TLS Optimization

Implement modern SSL/TLS protocols and ciphers. Enable OCSP stapling for faster certificate validation and session resumption/tickets for quicker subsequent connections.

SSL Configuration Best Practices

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name your-domain.com www.your-domain.com;

    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    # Modern TLS protocols
    ssl_protocols TLSv1.2 TLSv1.3;

    # Strong cipher suites (order matters)
    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_prefer_server_ciphers on;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s; # Use your preferred DNS resolvers
    resolver_timeout 5s;

    # Session resumption
    ssl_session_cache shared:SSL:10m; # 10MB cache size
    ssl_session_timeout 10m; # 10 minutes
    ssl_session_tickets on;

    # HSTS (HTTP Strict Transport Security) - uncomment after testing
    # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # ... other server configurations ...
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name your-domain.com www.your-domain.com;
    return 301 https://$host$request_uri;
}

Gunicorn Tuning for Python-based WooCommerce (e.g., Django/Flask)

When running a Python backend for WooCommerce, Gunicorn is a popular choice. Its configuration directly impacts how many requests your application can handle concurrently and how efficiently it uses server resources.

Worker Processes and Threads

The core of Gunicorn tuning lies in its worker processes and, if applicable, threads. For CPU-bound tasks, more processes are generally better. For I/O-bound tasks, threads can be more efficient. WooCommerce often involves a mix, so experimentation is key.

Gunicorn Command-Line Arguments

# Example Gunicorn command for a Django application
# Adjust workers and threads based on your server's CPU and memory.
# A common starting point: (2 * num_cores) + 1 workers, and 1 thread per worker.

# For a 4-core CPU, a good starting point might be 9 workers, 1 thread.
# If your application is heavily I/O bound, you might experiment with more threads per worker.

gunicorn --workers 9 \
         --threads 1 \
         --bind 0.0.0.0:8000 \
         --timeout 120 \
         --graceful-timeout 120 \
         --log-level info \
         --access-logfile /var/log/gunicorn/access.log \
         --error-logfile /var/log/gunicorn/error.log \
         your_project.wsgi:application

Explanation:

  • --workers: The number of worker processes. A common heuristic is (2 * CPU_CORES) + 1.
  • --threads: The number of threads per worker. If your application is I/O bound (e.g., waiting for database or external APIs), increasing threads can improve concurrency without adding significant overhead. For CPU-bound tasks, keep this at 1.
  • --bind: The address and port Gunicorn listens on. This is typically proxied by Nginx.
  • --timeout: The number of seconds Gunicorn will wait for a worker to respond before considering the request timed out. WooCommerce requests can sometimes be long-running (e.g., complex product queries, checkout process).
  • --graceful-timeout: Timeout for graceful worker shutdown.
  • --log-level: Controls the verbosity of logging.
  • --access-logfile, --error-logfile: Essential for monitoring and debugging.

Worker Types

Gunicorn supports several worker types. For most Python applications, the sync worker (default) is suitable. For I/O-bound applications, gevent or eventlet can offer better concurrency by using asynchronous I/O.

Choosing a Worker Type

# Using gevent workers (requires installing gevent: pip install gevent)
gunicorn --worker-class gevent \
         --workers 9 \
         --bind 0.0.0.0:8000 \
         your_project.wsgi:application

Note: If using gevent or eventlet, the --threads option is ignored. The concurrency is managed by the event loop.

PHP-FPM Tuning for PHP-based WooCommerce

For traditional WordPress/WooCommerce installations, PHP-FPM is the standard FastCGI Process Manager. Its configuration dictates how PHP processes are managed, directly impacting performance and stability.

Process Manager Settings

PHP-FPM offers different process management strategies: static, dynamic, and ondemand. dynamic is often a good balance, allowing FPM to scale processes based on load while not keeping too many idle.

`php-fpm.conf` and Pool Configuration

; Example PHP-FPM pool configuration (e.g., /etc/php/8.1/fpm/pool.d/www.conf)

[www]
; User and group to run processes as
user = www-data
group = www-data

; Listen on a Unix socket or TCP/IP port
; For Nginx proxying, a socket is generally faster and more secure
listen = /run/php/php8.1-fpm.sock
; listen = 127.0.0.1:9000

; Process manager settings
; pm = dynamic ; options: static, dynamic, ondemand
pm = dynamic

; For pm = dynamic:
; pm.max_children: Maximum number of children that can be started.
; pm.start_servers: Number of children created at startup.
; pm.min_spare_servers: Minimum number of idle respawned children.
; pm.max_spare_servers: Maximum number of idle respawned children.
; pm.max_requests: Maximum number of requests each child process should serve.

; Adjust these values based on your server's CPU and RAM.
; A common starting point for pm.max_children is (total RAM / average process size).
; For a 4GB RAM server, and assuming PHP processes take ~30MB, max_children could be around 100.
; However, consider WordPress/WooCommerce memory usage. Start lower and monitor.
pm.max_children = 100
pm.start_servers = 5
pm.min_spare_servers = 2
pm.max_spare_servers = 10
pm.max_requests = 500

; For pm = static:
; pm.max_children = 50 ; Fixed number of children

; For pm = ondemand:
; pm.max_children = 50
; pm.process_idle_timeout = 10s ; Timeout for idle processes

; Request termination timeout
request_terminate_timeout = 120s

; Request slowlog (useful for debugging)
; slowlog = /var/log/php/php-fpm-slow.log
; request_slowlog_timeout = 10s

; Other settings
; rlimit_files = 1024
; rlimit_nofile = 65535
; chroot = /var/www/html
; catch_workers_output = yes
; env[PATH] = /usr/local/bin:/usr/bin:/bin

Tuning Strategy:

  • pm.max_children: This is the most critical setting. Too high, and you’ll run out of memory. Too low, and you’ll have requests queuing up. Monitor your server’s memory usage (e.g., using htop or free -m) and PHP-FPM’s process count. A good starting point is to estimate the average memory usage of a PHP process (e.g., 20-50MB for WordPress) and divide your total available RAM by this figure, then subtract memory for the OS and other services.
  • pm.start_servers, pm.min_spare_servers, pm.max_spare_servers: These control how quickly PHP-FPM can scale up to meet demand. For dynamic PM, ensure there are enough spare servers to handle sudden traffic spikes without excessive delay.
  • pm.max_requests: Setting this to a reasonable number (e.g., 500-1000) helps prevent memory leaks in long-running processes by forcing them to restart periodically.
  • request_terminate_timeout: Crucial for WooCommerce, as some operations can take time. Set this high enough to avoid premature timeouts, but not so high that it masks underlying performance issues.

PHP Configuration (`php.ini`)

Beyond PHP-FPM’s process management, core PHP settings in php.ini also play a role.

Key `php.ini` Directives

; Example php.ini settings (e.g., /etc/php/8.1/fpm/php.ini)

memory_limit = 256M
max_execution_time = 120
max_input_vars = 3000
upload_max_filesize = 64M
post_max_size = 64M
session.gc_maxlifetime = 14400 ; 4 hours
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.validate_timestamps=1
opcache.enable_cli=1

Notes:

  • memory_limit: Ensure this is sufficient for WordPress and WooCommerce operations, especially during checkout or complex product filtering.
  • max_execution_time: Align with Gunicorn’s timeout or PHP-FPM’s request_terminate_timeout.
  • max_input_vars: WooCommerce can generate many POST variables. Increasing this prevents “too many input variables” errors.
  • OPcache: Absolutely essential for PHP performance. Tune opcache.memory_consumption and opcache.max_accelerated_files based on your application size and available memory. opcache.revalidate_freq controls how often PHP checks for file updates; set to 0 in production if you manually clear cache after deploys, or a higher value (e.g., 60 seconds) for development/staging.

Redis for WooCommerce Caching and Session Management

Redis is an invaluable tool for WooCommerce, primarily for object caching (WordPress Transients API) and session storage. This offloads database reads and speeds up user experience.

Redis Configuration (`redis.conf`)

Key settings revolve around memory management, persistence, and network configuration.

# General Settings
daemonize yes
pidfile /var/run/redis/redis-server.pid
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300

# Memory Management
# Set a memory limit. Crucial for preventing Redis from consuming all available RAM.
# Example: 2GB
maxmemory 2gb
# Choose a memory eviction policy. For caching, volatile-lru or allkeys-lru are common.
# volatile-lru: Evict using LRU approximation among keys with an expire set.
# allkeys-lru: Evict using LRU approximation among all keys.
maxmemory-policy allkeys-lru

# Persistence (Optional, depending on use case)
# For caching and sessions, persistence might not be strictly necessary,
# but can be useful for recovery. RDB is generally sufficient.
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis

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

# Security
# Consider binding to a specific interface or using a firewall.
# bind 127.0.0.1 -::1
# requirepass your_strong_redis_password
# rename-command CONFIG "" # Disable dangerous commands if not needed

# Replication (if using Redis Sentinel or Cluster)
# masterauth your_strong_redis_password
# replica-serve-stale-data yes
# replica-read-only yes

Tuning Considerations:

  • maxmemory: This is paramount. Set it to a value that leaves ample RAM for your OS, Nginx, and application servers. Monitor Redis memory usage with redis-cli INFO memory.
  • maxmemory-policy: For caching, LRU (Least Recently Used) policies are effective. If Redis is also used for critical data that must not be evicted, consider a different policy or a larger maxmemory.
  • Persistence: If Redis is solely for caching and sessions that can be regenerated, you can disable persistence (comment out all save lines) or use a less frequent save schedule to reduce disk I/O.
  • Security: Always bind Redis to localhost or a private network interface and use a strong password (requirepass).

Integration with WordPress/WooCommerce

Ensure your WordPress installation is configured to use Redis. This typically involves a plugin like “Redis Object Cache” or “W3 Total Cache” with Redis enabled.

Example `wp-config.php` Snippet (for Redis Object Cache plugin)

// Ensure Redis Object Cache plugin is installed and activated.
// The plugin typically handles the connection details.
// If you need to manually configure, you might add something like this:

define('WP_REDIS_CLIENT', 'phpredis'); // Or 'credis' if phpredis is not available
define('WP_REDIS_HOST', '127.0.0.1'); // Or your Redis server IP/hostname
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_PASSWORD', 'your_strong_redis_password'); // If password is set
define('WP_REDIS_TIMEOUT', 1);
define('WP_REDIS_READ_TIMEOUT', 1);
define('WP_REDIS_DATABASE', 0); // Use database 0 for object cache

// For session handling (if using a separate plugin or custom solution)
// define('WC_SESSION_ENGINE', 'redis');
// define('WC_REDIS_SESSION_HOST', '127.0.0.1');
// define('WC_REDIS_SESSION_PORT', 6379);
// define('WC_REDIS_SESSION_PASSWORD', 'your_strong_redis_password');
// define('WC_REDIS_SESSION_DATABASE', 1); // Use a different DB for sessions

Monitoring and Iterative Tuning

Performance tuning is not a one-time task. Continuous monitoring is essential to identify bottlenecks and adjust configurations. Use tools like:

  • AWS CloudWatch: Monitor EC2 instance metrics (CPU Utilization, Network In/Out, Disk I/O), RDS metrics (if applicable), and ElastiCache metrics (CPU, Memory, Evictions, Cache Hits/Misses).
  • Nginx Status Module: stub_status provides real-time connection and request statistics.
  • Gunicorn/PHP-FPM Logs: Analyze access and error logs for slow requests or errors.
  • Redis CLI: Use redis-cli INFO, redis-cli MEMORY STATS, and redis-cli SLOWLOG GET.
  • Application Performance Monitoring (APM) Tools: Services like New Relic, Datadog, or Sentry can provide deep insights into application-level performance, database queries, and external API calls.
  • Load Testing Tools: Tools like ApacheBench (ab), k6, or JMeter can simulate traffic to test the impact of your tuning changes under load.

Iteratively adjust worker counts, timeouts, memory limits, and caching strategies based on observed performance metrics and load test results. Document all changes and their impact.

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

  • Disaster Recovery 101: Architecting Auto-Failovers for Redis and PHP Deployments on OVH
  • How We Audited a High-Traffic WooCommerce Enterprise Stack on Google Cloud and Mitigated Race conditions during high-concurrency payment processing
  • Disaster Recovery 101: Architecting Auto-Failovers for Elasticsearch and Magento 2 Deployments on DigitalOcean
  • An Auditor’s Checklist for Securing WordPress Backends on OVH
  • Step-by-Step: Diagnosing Perl script high CPU throttling due to unoptimized regular expressions on AWS Servers

Copyright © 2026 · Vinay Vengala