• 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 MySQL on DigitalOcean for WooCommerce

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

Nginx as a High-Performance Frontend for WooCommerce

For a WooCommerce site, Nginx serves as an ideal frontend, efficiently handling static assets, SSL termination, and request routing to your application server (Gunicorn for Python/Django or PHP-FPM for PHP). Optimizing Nginx is crucial for minimizing latency and maximizing throughput.

Core Nginx Configuration Tuning

The primary Nginx configuration file, typically located at /etc/nginx/nginx.conf, contains global settings. We’ll focus on key directives that impact performance.

Worker Processes and Connections: The worker_processes directive should generally be set to the number of CPU cores available. worker_connections defines the maximum number of simultaneous connections a worker process can handle. A common starting point is auto for worker_processes, allowing Nginx to determine the optimal number, and a generous value for worker_connections, such as 4096 or higher, depending on expected load.

Example nginx.conf Snippet

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 4096;
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

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

    # SSL Settings
    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';

    # Gzip Compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;

    # Caching for Static Assets
    # This should be configured more granularly in your site's server block
    # For example, for static files like CSS, JS, images:
    # location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
    #     expires 30d;
    #     add_header Cache-Control "public, no-transform";
    # }

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Site-Specific Server Block Optimization

Within your WooCommerce site’s server block configuration (e.g., /etc/nginx/sites-available/your-site.conf), further optimizations are critical. This includes caching, request buffering, and proxy settings.

Caching Static Assets

Leverage browser caching for static assets to reduce server load and improve page load times for repeat visitors. Set appropriate expires headers.

server {
    # ... other server directives ...

    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|webp|woff|woff2|ttf|eot)$ {
        expires 365d; # Cache for a year
        add_header Cache-Control "public, immutable";
        access_log off; # Don't log access for static files
        log_not_found off;
    }

    # ... other location blocks ...
}

Optimizing Proxy Buffers (for Gunicorn/PHP-FPM)

When proxying requests to your application server, Nginx uses buffers. Incorrectly sized buffers can lead to upstream sent too big header errors or performance bottlenecks. For dynamic content, you’ll want to ensure these are adequately sized.

server {
    # ... other server directives ...

    location / {
        proxy_pass http://your_app_backend; # e.g., http://127.0.0.1:8000 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;

        # Proxy buffer settings
        proxy_buffer_size 16k;
        proxy_buffers 4 32k;
        proxy_busy_buffers_size 64k;
        proxy_temp_file_write_size 64k;

        proxy_read_timeout 300s; # Increase timeout for potentially long requests
        proxy_connect_timeout 75s;
        proxy_send_timeout 300s;
    }

    # ... other location blocks ...
}

SSL/TLS Optimization

SSL/TLS handshake can be CPU-intensive. Optimizing SSL settings reduces this overhead. Enabling session caching and using modern, efficient cipher suites are key.

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

    server_name your-domain.com;

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

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

    # ... rest of your server block ...
}

Gunicorn Tuning for Python/Django WooCommerce

When running a Python-based WooCommerce (e.g., Django with a WooCommerce-like API or a custom solution), Gunicorn is a popular WSGI HTTP Server. Its configuration directly impacts application responsiveness and concurrency.

Key Gunicorn Worker Settings

The most impactful settings relate to worker processes and threads. For CPU-bound tasks, more worker processes are beneficial. For I/O-bound tasks, worker threads can improve concurrency.

Worker Type and Count

Gunicorn supports different worker types:

  • sync: The default. Each worker handles one request at a time. Simple but can block under heavy I/O.
  • gevent: Uses green threads for asynchronous I/O. Excellent for I/O-bound workloads.
  • eventlet: Similar to gevent, also uses green threads.
  • gthread: Uses Python’s native threading. Can be simpler to debug than gevent/eventlet but subject to the GIL for CPU-bound tasks.

A common recommendation for a balanced workload is to use gevent workers. The number of workers is typically calculated as (2 * Number of CPU Cores) + 1. For threads (if using gthread or gevent with thread support), a value of 2-4 per worker is often a good starting point.

Example Gunicorn Command Line / Systemd Service

Assuming your Django app’s WSGI entry point is myproject.wsgi:application and you’re running on a DigitalOcean droplet with 4 CPU cores:

# Example using gunicorn command line
gunicorn --workers 9 \
         --worker-class gevent \
         --threads 2 \
         --bind 0.0.0.0:8000 \
         --timeout 120 \
         --graceful-timeout 120 \
         myproject.wsgi:application

# Example Systemd service file (/etc/systemd/system/gunicorn.service)
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=your_app_user
Group=www-data
WorkingDirectory=/path/to/your/project
Environment="PATH=/path/to/your/venv/bin"
ExecStart=/path/to/your/venv/bin/gunicorn \
    --workers 9 \
    --worker-class gevent \
    --threads 2 \
    --bind unix:/run/gunicorn.sock \
    --timeout 120 \
    --graceful-timeout 120 \
    myproject.wsgi:application

[Install]
Section=multi-user.target

Note: Using a Unix socket (unix:/run/gunicorn.sock) is generally preferred for performance and security when Nginx is on the same server, as it avoids TCP overhead.

Gunicorn Timeout Settings

--timeout: The number of seconds Gunicorn will wait at maximum for a worker to process a given request. If a request takes longer, the worker is killed and a new one is spawned. For WooCommerce, which can have complex product queries or order processing, increasing this value (e.g., to 120 seconds) is often necessary.

PHP-FPM Tuning for PHP-Based WooCommerce

For traditional PHP-based WooCommerce installations, PHP-FPM (FastCGI Process Manager) is the standard. Optimizing its configuration is paramount for handling concurrent PHP requests.

PHP-FPM Pool Configuration

PHP-FPM pools are defined in configuration files, typically located in /etc/php/[version]/fpm/pool.d/www.conf. The key settings control how PHP processes are managed.

Process Management (`pm`)

PHP-FPM offers three process management strategies:

  • static: A fixed number of child processes are spawned. Predictable but can be inefficient if load fluctuates.
  • dynamic: Starts with a few processes and spawns more as needed, up to a `pm.max_children` limit. Processes are killed when idle.
  • ondemand: Starts no processes initially. Processes are spawned only when a request arrives and are killed after a period of inactivity. Lowest memory footprint but can have higher latency for the first request.

For most WooCommerce sites, dynamic is a good balance. For very high-traffic sites with predictable loads, static might offer slightly better performance. ondemand is best for low-traffic sites or those with extreme memory constraints.

Tuning `dynamic` Process Management

When using pm = dynamic, these directives are crucial:

  • pm.max_children: The maximum number of child processes that will be spawned. This is the most critical setting. Set it based on your server’s RAM. A common rule of thumb is to allow ~20-30MB per PHP-FPM worker process. For a 2GB RAM droplet, you might aim for 50-70.
  • pm.start_servers: The number of child processes to start when PHP-FPM starts.
  • pm.min_spare_servers: The minimum number of “spare” (idle) processes that should be kept alive.
  • pm.max_spare_servers: The maximum number of “spare” (idle) processes.
  • pm.max_requests: The number of requests each child process should execute before respawning. This helps prevent memory leaks. A value between 500 and 1000 is typical.

Example www.conf Snippet (PHP 8.x)

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

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

pm = dynamic
pm.max_children = 70
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 750

; Request handling
request_terminate_timeout = 120s ; Match Nginx proxy_read_timeout
; rlimit_files = 1024
; rlimit_nofile = 65536

; Performance tuning
; 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

Note: Ensure OPcache is enabled and configured appropriately in your php.ini for significant performance gains. The commented-out lines in the example show typical OPcache settings.

PHP-FPM and Nginx Communication

Ensure your Nginx site configuration correctly points to the PHP-FPM socket or address. For example, in your /etc/nginx/sites-available/your-site.conf:

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    # With php-fpm (or other unix sockets):
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
    # With php-cgi (or other tcp sockets):
    # fastcgi_pass 127.0.0.1:9000;

    # Increase FastCGI buffer sizes if you encounter issues with large POST requests or responses
    fastcgi_buffer_size 16k;
    fastcgi_buffers 4 32k;
    fastcgi_busy_buffers_size 64k;
}

MySQL/MariaDB Performance Tuning for WooCommerce

The database is often the bottleneck for dynamic e-commerce sites like WooCommerce. Optimizing MySQL/MariaDB involves tuning its configuration and ensuring efficient queries.

Key MySQL Configuration Directives

The primary configuration file is typically /etc/mysql/my.cnf or /etc/mysql/mysql.conf.d/mysqld.cnf. The most impactful settings relate to memory buffers and connection handling.

InnoDB Buffer Pool

innodb_buffer_pool_size: This is the most critical setting for InnoDB. It caches data and indexes. For a dedicated database server, setting this to 70-80% of available RAM is common. For a shared server, adjust accordingly. For a 2GB RAM droplet, 1GB (1024M) is a reasonable starting point.

Connection Handling

max_connections: The maximum number of simultaneous client connections. WooCommerce sites can have many connections, especially with plugins. Start with 150-200 and monitor. Too high can exhaust server resources.

Query Cache (Deprecated/Removed in newer MySQL versions)

query_cache_size and query_cache_type: While historically useful, the query cache is often disabled in modern MySQL/MariaDB versions (removed in MySQL 8.0) due to scalability issues and contention. If you are on an older version and see benefits, configure it cautiously. For most modern deployments, it’s best left disabled.

Log File Settings

innodb_log_file_size and innodb_log_buffer_size: These affect write performance. Larger log files can improve write throughput but increase recovery time after a crash. A common starting point for innodb_log_file_size is 256M or 512M. innodb_log_buffer_size can be set to 16M or 32M.

Example mysqld.cnf Snippet

[mysqld]
# General Settings
user                    = mysql
pid-file                = /var/run/mysqld/mysqld.pid
socket                  = /var/run/mysqld/mysqld.sock
port                    = 3306
basedir                 = /usr
datadir                 = /var/lib/mysql
tmpdir                  = /tmp
lc_messages_dir         = /usr/share/mysql
lc_messages             = en_US

# InnoDB Settings
innodb_buffer_pool_size         = 1024M  # Adjust based on RAM (e.g., 70-80% of RAM for dedicated DB server)
innodb_log_file_size            = 512M   # Affects write performance
innodb_log_buffer_size          = 32M    # For large transactions
innodb_flush_method             = O_DIRECT # Recommended for performance on Linux
innodb_flush_log_at_trx_commit  = 1      # Strict ACID compliance, can impact write speed. 2 for slightly better performance.
innodb_file_per_table           = 1      # Recommended for manageability

# Connection Settings
max_connections                 = 200    # Adjust based on load and server resources
thread_cache_size               = 16     # Cache threads for reuse

# Other Performance Settings
table_open_cache                = 2000
table_definition_cache          = 1000
query_cache_type                = 0      # Disable query cache (if using MySQL 5.7 or older, consider carefully)
query_cache_size                = 0
sort_buffer_size                = 2M
join_buffer_size                = 2M
read_rnd_buffer_size            = 2M
read_buffer_size                = 1M

# Logging (Optional, for debugging)
# log_error = /var/log/mysql/error.log
# slow_query_log = 1
# slow_query_log_file = /var/log/mysql/mysql-slow.log
# long_query_time = 2

Important: After modifying my.cnf, you must restart the MySQL service: sudo systemctl restart mysql.

Query Optimization and Indexing

Even with a well-tuned server, inefficient queries will cripple performance. Regularly analyze slow queries using MySQL’s slow query log. Ensure that common query patterns in WooCommerce (product lookups, order retrieval, user data) are supported by appropriate indexes.

Example Slow Query Analysis

Enable the slow query log in your my.cnf (as shown in the example above) and then use tools like mysqldumpslow or pt-query-digest from Percona Toolkit to analyze the log file.

# Example using mysqldumpslow
sudo mysqldumpslow /var/log/mysql/mysql-slow.log

# Example using pt-query-digest (requires Percona Toolkit installation)
sudo pt-query-digest /var/log/mysql/mysql-slow.log > /tmp/slow_query_analysis.txt
cat /tmp/slow_query_analysis.txt

Look for queries that are performing full table scans or have a high number of rows examined. Use EXPLAIN in MySQL to understand query execution plans and identify missing indexes.

EXPLAIN SELECT * FROM wp_posts WHERE post_type = 'product' AND post_status = 'publish';
-- Analyze the output for 'type' (should ideally be 'ref', 'eq_ref', 'range', not 'ALL')
-- and 'rows' (number of rows examined).

Database Maintenance

Regularly run OPTIMIZE TABLE on frequently updated tables (especially those with frequent deletes or updates) to reclaim space and improve performance. For WooCommerce, this might include tables related to orders, sessions, or product meta data.

OPTIMIZE TABLE wp_posts;
OPTIMIZE TABLE wp_options;
OPTIMIZE TABLE wp_wc_order_stats;
-- ... and other relevant tables

Putting It All Together: Monitoring and Iteration

Performance tuning is not a one-time task. Continuous monitoring is essential. Utilize tools like:

  • Nginx: stub_status module, Nginx Amplify, Prometheus/Grafana with `nginx-exporter`.
  • Gunicorn: Built-in logging, Prometheus/Grafana with `gunicorn-exporter` or custom metrics.
  • PHP-FPM: pm.status_path, New Relic, Datadog, Prometheus/Grafana with `php-fpm-exporter`.
  • MySQL: SHOW GLOBAL STATUS, SHOW ENGINE INNODB STATUS, Percona Monitoring and Management (PMM), Prometheus/Grafana with `mysqld_exporter`.
  • Application-level: New Relic, Datadog, Sentry for error tracking and performance profiling.

Start with conservative settings, monitor your system’s resource utilization (CPU, RAM, I/O, network), and gradually increase parameters as needed. Benchmark your site before and after changes to quantify improvements. For WooCommerce, consider the impact of plugins, themes, and third-party integrations, as they can significantly affect performance and may require specific tuning or optimization.

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