• 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 PostgreSQL on OVH for WooCommerce

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and PostgreSQL on OVH for WooCommerce

Nginx Configuration for High-Traffic WooCommerce

Optimizing Nginx is paramount for serving static assets efficiently and acting as a robust reverse proxy for your WooCommerce application. For OVH environments, especially those with dedicated resources, fine-tuning Nginx can significantly reduce latency and improve throughput.

We’ll focus on key directives that impact performance and stability. This configuration assumes a typical setup where Nginx handles SSL termination, serves static files, and forwards dynamic requests to a backend application server (Gunicorn for Python/Django/Flask or PHP-FPM for PHP).

Core Nginx Performance Directives

The nginx.conf file, typically located at /etc/nginx/nginx.conf or within /etc/nginx/conf.d/, is our starting point. We’ll adjust worker processes, connections, and caching mechanisms.

Worker Processes and Connections

The worker_processes directive should ideally be set to the number of CPU cores available on your server. For OVH dedicated servers, this is easily identifiable. The worker_connections directive defines the maximum number of simultaneous connections that each worker process can handle. A common starting point is 1024 or higher, depending on your expected load.

Tuning Keep-Alive and Buffers

keepalive_timeout controls how long an idle connection will remain open. A moderate value (e.g., 65 seconds) can reduce the overhead of establishing new TCP connections. client_body_buffer_size and client_header_buffer_size are crucial for handling large requests and headers. For WooCommerce, which can involve large product images and AJAX requests, increasing these might be necessary.

Enabling Gzip Compression

Gzip compression significantly reduces the size of text-based assets (HTML, CSS, JS, JSON), leading to faster load times. Ensure it’s enabled and configured appropriately.

Example Nginx Configuration Snippet

This snippet demonstrates key optimizations within the http block. Adapt worker_processes to your server’s core count.

# /etc/nginx/nginx.conf

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

events {
    worker_connections 4096; # Adjust based on 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

    # Buffers for large requests
    client_body_buffer_size 10M; # Adjust as needed
    client_header_buffer_size 1M; # Adjust as needed
    large_client_header_buffers 4 1M; # Adjust as needed

    # Gzip Compression
    gzip on;
    gzip_disable "msie6";
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6; # Compression level (1-9)
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    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;

    # SSL Configuration (example for a site)
    # include /etc/nginx/sites-enabled/*; # Or define directly

    # Proxying to backend (e.g., Gunicorn or PHP-FPM)
    # proxy_pass http://unix:/path/to/your/app.sock; # For Gunicorn/uWSGI
    # proxy_pass http://127.0.0.1:9000; # For PHP-FPM

    # Other directives like caching, rate limiting, etc.
}

WooCommerce Specific Nginx Optimizations

For WooCommerce, we need to consider caching static assets aggressively and potentially setting up specific rules for media files.

Static Asset Caching

Leverage browser caching for static assets like CSS, JavaScript, and images. This reduces server load and speeds up repeat visits.

# Inside your server block or http block

location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|webp)$ {
    expires 365d; # Cache for 1 year
    add_header Cache-Control "public, no-transform";
    access_log off; # Optionally disable logging for static assets
    log_not_found off;
}

# Cache fonts
location ~* \.(woff|woff2|ttf|eot)$ {
    expires 365d;
    add_header Cache-Control "public, no-transform";
    access_log off;
    log_not_found off;
}

Disabling Access Logging for Static Files

For high-traffic sites, the access log can become a bottleneck. Disabling it for static assets can improve performance.

# Within your static asset location blocks:
access_log off;
log_not_found off;

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

When using Python frameworks like Django or Flask for a WooCommerce backend, Gunicorn is a popular choice for serving the application. Proper Gunicorn configuration is crucial for handling concurrent requests and managing worker processes effectively.

Gunicorn Worker Types and Scaling

Gunicorn supports several worker types. For most I/O-bound web applications, the gevent or event worker classes are recommended. The number of workers is a critical tuning parameter. A common heuristic is (2 * number_of_cores) + 1, but this should be adjusted based on your application’s behavior and server resources.

Worker Timeout and Max Requests

--timeout specifies the maximum time a worker can spend on a request before being restarted. For long-running operations (e.g., complex report generation, large data imports), this might need to be increased. However, excessively high timeouts can mask performance issues. --max-requests is a vital directive for preventing memory leaks by restarting workers after a certain number of requests. This is a proactive measure against gradual performance degradation.

Gunicorn Configuration Example

This example shows how to launch Gunicorn with optimized settings, typically from a systemd service file or a shell script.

# Example command to start Gunicorn
# Assuming your Django app is in a directory 'myproject' and the WSGI application is 'myproject.wsgi:application'

gunicorn --workers 4 \
         --worker-class gevent \
         --bind unix:/path/to/your/app.sock \
         --timeout 120 \
         --max-requests 5000 \
         --log-level info \
         --access-logfile /var/log/gunicorn/access.log \
         --error-logfile /var/log/gunicorn/error.log \
         myproject.wsgi:application

Explanation of Directives:

  • --workers 4: Adjust based on your CPU cores. For a 4-core server, 5 workers (2*4 + 1) might be a good starting point.
  • --worker-class gevent: Uses gevent for asynchronous I/O, suitable for I/O-bound tasks common in web applications.
  • --bind unix:/path/to/your/app.sock: Binds Gunicorn to a Unix socket, which is generally faster than TCP/IP for local communication between Nginx and Gunicorn.
  • --timeout 120: Sets a 120-second timeout for worker requests. Tune this carefully.
  • --max-requests 5000: Restarts workers after 5000 requests to mitigate memory leaks.
  • --log-level info: Sets the logging level.
  • --access-logfile & --error-logfile: Specifies log file locations. Ensure the directories exist and are writable by the Gunicorn user.

Systemd Service File for Gunicorn

Managing Gunicorn with systemd ensures it runs as a service, starts on boot, and restarts automatically if it crashes.

# /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
ExecStart=/path/to/your/venv/bin/gunicorn \
          --workers 4 \
          --worker-class gevent \
          --bind unix:/path/to/your/app.sock \
          --timeout 120 \
          --max-requests 5000 \
          --log-level info \
          --access-logfile /var/log/gunicorn/access.log \
          --error-logfile /var/log/gunicorn/error.log \
          myproject.wsgi:application

[Install]
WantedBy=multi-user.target

After creating this file, run: sudo systemctl daemon-reload, sudo systemctl start gunicorn, and sudo systemctl enable gunicorn.

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 critical for handling concurrent PHP requests efficiently.

PHP-FPM Process Manager Settings

The pm (Process Manager) setting determines how PHP-FPM manages worker processes. The most common options are static, dynamic, and ondemand.

  • static: Pre-forks a fixed number of child processes. Good for predictable loads and dedicated servers where you can precisely allocate resources.
  • dynamic: Starts a few children initially and spawns more as needed, up to a defined maximum. It also kills idle children to save resources. This is often a good balance.
  • ondemand: Starts no children initially and spawns them only when a request comes in. Can save resources but might introduce slight latency on the first request.

For OVH dedicated servers, static or dynamic with carefully tuned parameters are usually preferred for consistent performance.

Key PHP-FPM Directives

These directives are found in the PHP-FPM pool configuration file, typically located at /etc/php/[version]/fpm/pool.d/www.conf.

; /etc/php/[version]/fpm/pool.d/www.conf

[www]
user = www-data
group = www-data
listen = /run/php/php[version]-fpm.sock ; Or TCP/IP: listen = 127.0.0.1:9000
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 100       ; Max number of children at any point. Adjust based on RAM.
pm.start_servers = 10       ; Number of children to start when PHP-FPM starts.
pm.min_spare_servers = 5    ; Min number of idle servers.
pm.max_spare_servers = 20   ; Max number of idle servers.
pm.process_idle_timeout = 10s ; Timeout for killing idle processes (if pm.max_spare_servers is reached).

pm.max_requests = 5000      ; Max requests per child process before respawning. Crucial for memory leak prevention.

request_terminate_timeout = 120s ; Max execution time for a single script.
request_slowlog_timeout = 30s    ; Log scripts that take longer than this.

; Other settings
catch_workers_output = yes
;slowlog = /var/log/php/php-fpm-slow.log

Tuning Considerations:

  • pm.max_children: This is the most critical setting. It should be calculated based on your server’s available RAM. A common formula is: (Total RAM - RAM used by OS/other services) / Average RAM per PHP process. Monitor memory usage closely.
  • pm.max_requests: Similar to Gunicorn’s --max-requests, this prevents memory leaks by recycling worker processes.
  • request_terminate_timeout: Adjust based on the longest expected execution time for your WooCommerce operations.
  • request_slowlog_timeout: Enable and monitor the slow log to identify performance bottlenecks in your PHP code.

After modifying the PHP-FPM configuration, restart the service: sudo systemctl restart php[version]-fpm.

PostgreSQL Tuning for WooCommerce Databases

The PostgreSQL database is the backbone of WooCommerce. Optimizing its performance, particularly for read/write operations and query execution, is essential for a responsive store.

Key PostgreSQL Configuration Parameters

The primary configuration file is postgresql.conf, typically found in /etc/postgresql/[version]/main/.

Memory Allocation

shared_buffers: This is the most important parameter. It defines the amount of memory PostgreSQL uses for caching data. A common recommendation is 25% of system RAM, but for dedicated database servers, it can be pushed higher (up to 40-50% if the OS has enough memory for its own caching). For a 64GB RAM server, 16GB (16384MB) is a good starting point.

work_mem: Memory used for internal sort operations and hash tables. It’s allocated per sort/hash operation within a query. Setting this too high can lead to excessive memory usage if many complex queries run concurrently. Start conservatively (e.g., 16MB-64MB) and increase if `EXPLAIN ANALYZE` shows disk-based sorts.

maintenance_work_mem: Memory used for maintenance tasks like `VACUUM`, `CREATE INDEX`, and `ALTER TABLE`. A larger value can significantly speed up these operations. 256MB to 1GB is often suitable.

Checkpointing and WAL (Write-Ahead Logging)

max_wal_size and min_wal_size: These control the amount of WAL data that can accumulate before a checkpoint is triggered. Increasing these values reduces the frequency of checkpoints, which can improve write performance by reducing I/O spikes. However, it also increases recovery time after a crash.

checkpoint_completion_target: Spreads the checkpoint process over time, reducing I/O impact. A value of 0.9 is often recommended.

Example postgresql.conf Snippet

# /etc/postgresql/[version]/main/postgresql.conf

# General Settings
listen_addresses = '*' # Or specific IPs for security
port = 5432

# Memory Settings (Example for a 64GB RAM server)
shared_buffers = 16GB
work_mem = 32MB
maintenance_work_mem = 512MB

# Checkpointing & WAL
max_wal_size = 4GB
min_wal_size = 1GB
checkpoint_completion_target = 0.9
wal_buffers = 16MB # Typically 1/3 of shared_buffers, up to 16MB

# Connection Settings
max_connections = 200 # Adjust based on application needs and server resources
superuser_reserved_connections = 3

# Query Planner
effective_cache_size = 48GB # Roughly 75% of total RAM, assuming OS cache is also used

# Logging
log_destination = 'stderr'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_min_duration_statement = 250ms # Log queries taking longer than 250ms
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
log_temp_files = on
log_autovacuum_min_duration = 0 # Log all autovacuum activity

After modifying postgresql.conf, restart the PostgreSQL service: sudo systemctl restart postgresql.

Database Indexing and Vacuuming

Beyond configuration, proper indexing and regular maintenance are critical.

Essential Indexes for WooCommerce

WooCommerce relies heavily on the WordPress database structure. Ensure critical tables like wp_posts, wp_postmeta, wp_options, and wp_wc_order_stats have appropriate indexes. WordPress often creates basic indexes, but custom plugins or heavy usage might require more specific ones. Use EXPLAIN ANALYZE on slow queries to identify missing indexes.

-- Example: Checking indexes on wp_posts
SELECT
    tablename,
    indexname,
    indexdef
FROM
    pg_indexes
WHERE
    tablename = 'wp_posts';

-- Example: Creating a potential index (use with caution and after analysis)
-- CREATE INDEX idx_posts_post_type_date ON wp_posts (post_type, post_date);

Autovacuum Tuning

PostgreSQL’s autovacuum process is essential for reclaiming space from dead tuples and preventing table bloat. Ensure it’s enabled and tuned. The parameters in postgresql.conf (like autovacuum_vacuum_threshold, autovacuum_analyze_threshold, autovacuum_vacuum_scale_factor, autovacuum_analyze_scale_factor) control when vacuuming occurs. For busy WooCommerce databases, you might need to lower the scale factors or increase maintenance_work_mem.

Regularly monitor table bloat using queries like:

SELECT
    relname,
    n_live_tup,
    n_dead_tup,
    pg_size_pretty(pg_table_size(oid)) AS table_size,
    pg_size_pretty(pg_total_relation_size(oid)) AS total_size
FROM
    pg_stat_user_tables
WHERE
    n_dead_tup > 0
ORDER BY
    n_dead_tup DESC;

If bloat is significant, consider running manual `VACUUM FULL` (which locks tables) or `VACUUM` (non-blocking but less effective at reclaiming space) during maintenance windows, or further tune autovacuum parameters.

Monitoring and Iterative Tuning

Performance tuning is not a one-time task. Continuous monitoring and iterative adjustments are key. Utilize tools like:

  • Nginx: nginx -s reload for applying config changes. Check /var/log/nginx/access.log and error.log. Use ngx_http_stub_status_module for basic metrics.
  • Gunicorn/PHP-FPM: System logs (journalctl -u gunicorn or journalctl -u php[version]-fpm). Monitor process counts and memory usage via htop or top.
  • PostgreSQL: pg_stat_activity for current queries, pg_stat_statements extension for query performance analysis, pg_buffercache for buffer usage, and OS-level tools like iostat and vmstat.
  • Application Level: WooCommerce’s own performance tools, New Relic, Datadog, or similar APM solutions.

Start with conservative settings, monitor the impact, and gradually increase parameters like worker counts, buffer sizes, and timeouts as needed, always validating against your specific workload and server capabilities on OVH.

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