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

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and MySQL on Linode for WooCommerce

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

Nginx as a High-Performance Frontend for WooCommerce

For a WooCommerce site, Nginx serves as an exceptional frontend, efficiently handling static assets, SSL termination, and request routing to your backend application server (Gunicorn for Python/Django or PHP-FPM for PHP). Proper Nginx tuning is paramount for low latency and high throughput.

Nginx Worker Processes and Connections

The `worker_processes` directive dictates how many worker processes Nginx will spawn. A common recommendation is to set this to the number of CPU cores available on your server. `worker_connections` defines the maximum number of simultaneous connections that each worker process can handle. The total maximum connections will be `worker_processes * worker_connections`.

On a typical Linode instance, let’s assume 4 CPU cores. We’ll also set a generous `worker_connections` value, keeping in mind the system’s file descriptor limits.

Tuning nginx.conf

# /etc/nginx/nginx.conf

user www-data;
worker_processes 4; # Set to the 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 for security

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

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

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

    # SSL Configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    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';

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

After modifying nginx.conf, always test the configuration and reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

Optimizing File Descriptor Limits

The `worker_connections` value is ultimately limited by the system’s maximum open file descriptors. Ensure these are set appropriately for Nginx.

Tuning /etc/security/limits.conf

# /etc/security/limits.conf
# Increase limits for the nginx user

www-data soft nofile 65536
www-data hard nofile 65536
root soft nofile 65536
root hard nofile 65536

You might also need to adjust systemd service limits if Nginx is managed by systemd:

Tuning Systemd Service File

# Edit the Nginx systemd service file
sudo systemctl edit nginx.service

# Add the following lines to the override file:
[Service]
LimitNOFILE=65536
LimitNOFILESoft=65536

After these changes, you’ll need to restart Nginx for them to take effect:

sudo systemctl daemon-reload
sudo systemctl restart nginx

Caching Strategies with Nginx

Leveraging Nginx’s built-in caching capabilities can significantly reduce load on your backend and database. For WooCommerce, caching static assets and even dynamic content (with careful invalidation) is crucial.

Browser Caching

Set appropriate `Cache-Control` and `Expires` headers for static assets.

# In your Nginx server block or http context

location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|webp)$ {
    expires 365d;
    add_header Cache-Control "public, immutable";
}

FastCGI/Proxy Caching (for PHP-FPM or Gunicorn)

Nginx can cache responses from your backend application. This is powerful but requires careful consideration for dynamic content like WooCommerce product pages or cart contents.

Configuring Proxy Cache

# In your http context
proxy_cache_path /var/cache/nginx/my_cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;

# In your server block, within the location block proxying to your app
location / {
    proxy_pass http://your_app_backend; # e.g., http://127.0.0.1:8000 or http://unix:/var/run/php/php7.4-fpm.sock
    proxy_cache my_cache;
    proxy_cache_valid 200 302 10m; # Cache successful responses for 10 minutes
    proxy_cache_valid 404 1m;      # Cache 404s for 1 minute
    proxy_cache_key "$scheme$request_method$host$request_uri";
    add_header X-Cache-Status $upstream_cache_status;

    # Important for WooCommerce: Do NOT cache POST requests or requests with cookies
    proxy_cache_bypass $http_pragma $http_authorization;
    proxy_no_cache $http_pragma $http_authorization;

    # For PHP-FPM:
    # include snippets/fastcgi-php.conf;
    # fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;

    # For Gunicorn:
    # 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;
}

The proxy_cache_bypass and proxy_no_cache directives are critical for WooCommerce. They prevent caching of requests that involve user sessions, form submissions, or other dynamic elements that should not be served from a stale cache. You’ll need to carefully define which URIs or request conditions should bypass the cache (e.g., `/cart/`, `/checkout/`, `/my-account/`).

Gunicorn Tuning for Python/Django WooCommerce

When running a Python-based WooCommerce (e.g., using Django with a WooCommerce-like API or a custom solution), Gunicorn is a popular WSGI HTTP Server. Tuning Gunicorn involves adjusting worker processes and threads.

Gunicorn Worker Types and Scaling

Gunicorn offers several worker types:

  • Sync Workers (Default): Simple, but each worker can only handle one request at a time.
  • Async Workers (e.g., Gevent, Eventlet): Can handle multiple requests concurrently using non-blocking I/O and green threads. This is often preferred for I/O-bound applications like web servers.
  • Gevent Workers: A popular choice for I/O-bound tasks.

A common starting point for tuning is to use Gevent workers. The number of workers is typically set to `(2 * Number of CPU Cores) + 1`. The number of threads per worker depends on the application’s I/O patterns.

Gunicorn Configuration Example

You can configure Gunicorn via command-line arguments or a Python configuration file.

Command-line Configuration

# Assuming 4 CPU cores
# Using gevent workers for better concurrency
gunicorn --workers 9 --worker-class gevent --bind 0.0.0.0:8000 myapp.wsgi:application

Configuration File (e.g., gunicorn_config.py)

# gunicorn_config.py
import multiprocessing

# Number of worker processes. Typically (2 * num_cores) + 1
workers = (multiprocessing.cpu_count() * 2) + 1

# Use gevent for asynchronous handling
worker_class = 'gevent'

# Bind to a specific address and port
bind = '0.0.0.0:8000' # Or a Unix socket: 'unix:/path/to/your/app.sock'

# Other useful settings:
# accesslog = '/var/log/gunicorn/access.log'
# errorlog = '/var/log/gunicorn/error.log'
# loglevel = 'info' # or 'debug', 'warning', 'error', 'critical'
# timeout = 30 # seconds
# keepalive = 2 # seconds

To use the config file:

gunicorn -c gunicorn_config.py myapp.wsgi:application

PHP-FPM Tuning for PHP WooCommerce

For traditional PHP-based WooCommerce installations, PHP-FPM (FastCGI Process Manager) is the standard. Tuning PHP-FPM involves managing the pool of worker processes that handle PHP requests.

PHP-FPM Process Management Modes

PHP-FPM offers three primary process management modes:

  • Static: A fixed number of child processes are spawned when the FPM master process starts. This offers the most predictable performance but can be less efficient if load fluctuates wildly.
  • Dynamic: The number of child processes varies dynamically based on load. It starts with a minimum number and spawns more up to a maximum as needed.
  • On-Demand: Child processes are spawned only when a request arrives and are killed after a period of inactivity. This saves memory but can introduce latency for the first request after a period of idleness.

For a busy WooCommerce site, dynamic or static modes are generally preferred. Dynamic offers a good balance.

Tuning php-fpm.conf and Pool Configuration

The main configuration file is typically /etc/php/X.Y/fpm/php-fpm.conf (where X.Y is your PHP version), and pool configurations are in /etc/php/X.Y/fpm/pool.d/www.conf.

Example Pool Configuration (www.conf)

; /etc/php/X.Y/fpm/pool.d/www.conf

[www]
user = www-data
group = www-data
listen = /run/php/phpX.Y-fpm.sock ; Or a TCP socket like 127.0.0.1:9000

; Process Management (Dynamic Example)
pm = dynamic
pm.max_children = 50       ; Max number of children at any one time
pm.start_servers = 5       ; Number of children when FPM starts
pm.min_spare_servers = 2   ; Min number of idle respawns
pm.max_spare_servers = 10  ; Max number of idle respawns
pm.max_requests = 500      ; Max requests a child process will serve before respawning

; Process Management (Static Example - if load is very predictable)
; pm = static
; pm.max_children = 100 ; Fixed number of children

; Other important settings
; request_terminate_timeout = 120 ; Timeout for script execution (seconds)
; rlimit_files = 1024
; rlimit_nofile = 65536
; pm.process_idle_timeout = 10s ; For on-demand mode

; Error Reporting
; log_level = notice
; error_log = /var/log/php/phpX.Y-fpm.log
; access.log = /var/log/php/phpX.Y-fpm-access.log

Tuning `pm.max_children` is critical. It should be set based on your server’s RAM. A common formula is: `pm.max_children = (Total RAM – RAM for OS/other services) / Average RAM per PHP process`. You can monitor RAM usage and PHP-FPM process sizes to fine-tune this.

After changes, reload PHP-FPM:

sudo systemctl reload phpX.Y-fpm

MySQL/MariaDB Tuning for WooCommerce

The database is often the bottleneck for WooCommerce. Optimizing MySQL/MariaDB involves tuning buffer pools, query caches, and connection handling.

Key MySQL/MariaDB Configuration Parameters

These parameters are set in my.cnf or mysqld.cnf (location varies by distribution, often in /etc/mysql/ or /etc/mysql/mysql.conf.d/).

innodb_buffer_pool_size

This is arguably the most important setting for InnoDB. It’s the memory area where InnoDB caches table and index data. For a dedicated database server, setting this to 70-80% of available RAM is common. For a server running both web and DB, this needs to be much lower.

innodb_log_file_size and innodb_log_buffer_size

Larger log files can improve write performance by reducing flushing frequency, but increase recovery time after a crash. `innodb_log_buffer_size` caches log data before writing to disk.

max_connections

The maximum number of simultaneous client connections. WooCommerce and its plugins can open many connections. Set this high enough, but not so high that it exhausts server memory. Monitor `Threads_connected` status.

query_cache_size (Deprecated in MySQL 5.7, Removed in 8.0)

While useful in older versions, the query cache is often a source of contention and is disabled by default in newer MySQL versions. If you are on an older version and have many identical, read-heavy queries, it *might* help, but generally, it’s better to rely on application-level caching and proper indexing.

Example my.cnf Snippet (for a Linode with 8GB RAM, dedicated DB)

# /etc/mysql/my.cnf or /etc/mysql/mysql.conf.d/mysqld.cnf

[mysqld]
# General Settings
user                    = mysql
pid-file                = /var/run/mysqld/mysqld.pid
socket                  = /var/run/mysqld/mysqld.sock
datadir                 = /var/lib/mysql
log-error               = /var/log/mysql/error.log
# log_queries_not_using_indexes = 1 # Useful for debugging, but can be noisy

# InnoDB Settings
innodb_buffer_pool_size = 6G       ; ~75% of 8GB RAM, adjust based on actual usage
innodb_log_file_size    = 512M     ; Adjust based on write load
innodb_log_buffer_size  = 16M
innodb_flush_method     = O_DIRECT ; Recommended for Linux with hardware RAID/SSDs
innodb_flush_log_at_trx_commit = 1 ; Strict ACID compliance (can be 2 for performance if data loss risk is acceptable)
innodb_file_per_table   = 1        ; Recommended for better management

# Connection Settings
max_connections         = 200      ; Adjust based on application needs and server RAM
# thread_cache_size       = 16       ; Cache threads for reuse

# MyISAM Settings (if you still use MyISAM tables, though InnoDB is preferred)
# key_buffer_size         = 128M

# Temporary Tables
# tmp_table_size          = 64M
# max_heap_table_size     = 64M

# Sort Buffers
# sort_buffer_size        = 2M
# read_rnd_buffer_size    = 2M

After modifying the configuration, restart MySQL:

sudo systemctl restart mysql

Database Indexing and Query Optimization

Configuration tuning is only part of the story. Slow queries are a major performance killer for WooCommerce. Regularly analyze and optimize your database queries.

Identifying Slow Queries

Enable the slow query log in MySQL:

# In my.cnf under [mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 2 ; Log queries taking longer than 2 seconds
# log_queries_not_using_indexes = 1 ; Uncomment to log queries without indexes

Restart MySQL and then analyze the slow query log file. Tools like pt-query-digest from the Percona Toolkit are invaluable for summarizing these logs.

sudo apt-get install percona-toolkit # Or equivalent for your distro
pt-query-digest /var/log/mysql/mysql-slow.log > /var/log/mysql/mysql-slow-report.txt

Adding Indexes

Use `EXPLAIN` to understand query execution plans and identify missing indexes. For WooCommerce, common areas needing indexing include `wp_posts` (especially `post_type`, `post_status`), `wp_postmeta`, `wp_options`, and order-related tables.

-- Example: Analyzing a query
EXPLAIN SELECT * FROM wp_posts WHERE post_type = 'product' AND post_status = 'publish';

-- Example: Adding an index if needed
ALTER TABLE wp_posts ADD INDEX idx_post_type_status (post_type, post_status);

Be cautious with adding too many indexes, as they consume disk space and slow down write operations (INSERT, UPDATE, DELETE). Focus on indexes that significantly speed up frequent read queries.

Monitoring and Iteration

Performance tuning is an ongoing process. Implement robust monitoring to track key metrics and identify regressions or new bottlenecks.

Key Metrics to Monitor

  • Nginx: Active connections, requests per second, error rates (4xx, 5xx), cache hit/miss ratios.
  • Gunicorn/PHP-FPM: Worker utilization, request queue length, response times, memory usage per worker.
  • MySQL: Query throughput, slow queries, connection usage, buffer pool hit rate, disk I/O, CPU usage.
  • System: CPU load, RAM usage, disk I/O, network traffic.

Tools like Prometheus with Grafana, Datadog, New Relic, or even basic system tools like htop, iotop, and MySQL’s `SHOW GLOBAL STATUS` are essential.

Continuously analyze your monitoring data, identify slow points, adjust configurations incrementally, and re-test. This iterative approach ensures your WooCommerce site remains performant under varying loads.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Go Goroutines vs. Node.js Event Loop: Scaling I/O-Bound Microservices Under High Load
  • Elixir Phoenix vs. Go Gin: Concurrency Models and Fault Tolerance Under Peak Request Volume
  • Python Celery vs. Go Channels: Distributed Task Queue Overhead and Memory Reliability
  • Scala Pekko vs. Go Goroutines: Actor Model vs. CSP for Event-Driven Reactive Systems
  • Java Loom Virtual Threads vs. Go Goroutines: Under-the-Hood Scheduler and Thread Overhead Comparison

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (584)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (806)
  • PHP (5)
  • PHP Development (21)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (19)
  • Ruby on Rails (1)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (7)
  • WordPress Theme Development (357)

Recent Posts

  • Go Goroutines vs. Node.js Event Loop: Scaling I/O-Bound Microservices Under High Load
  • Elixir Phoenix vs. Go Gin: Concurrency Models and Fault Tolerance Under Peak Request Volume
  • Python Celery vs. Go Channels: Distributed Task Queue Overhead and Memory Reliability

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (806)
  • Debugging & Troubleshooting (584)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala