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

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

Nginx Configuration for High Throughput

Optimizing Nginx for a high-traffic environment on OVH requires a multi-faceted approach, focusing on connection handling, caching, and efficient static file serving. We’ll start with the core `nginx.conf` directives.

Worker Processes and Connections

The `worker_processes` directive should ideally be set to the number of CPU cores available on your server. This allows Nginx to utilize all available processing power for handling requests. The `worker_connections` directive defines the maximum number of simultaneous connections that each worker process can handle. A common starting point is 1024, but this can be increased significantly depending on your server’s RAM and the nature of your application’s traffic (e.g., long-polling vs. short requests).

worker_processes auto; # Or set to the number of CPU cores
events {
    worker_connections 4096; # Adjust based on RAM and traffic patterns
    multi_accept on;
    use epoll; # For Linux systems
}

Keepalive Connections

Enabling keepalive connections reduces the overhead of establishing new TCP connections for each HTTP request. The `keepalive_timeout` controls how long an idle keepalive connection will remain open. A value between 60 and 120 seconds is often a good balance, preventing resource exhaustion while still offering performance benefits. `keepalive_requests` limits the number of requests that can be served over a single keepalive connection.

http {
    # ... other http directives ...

    keepalive_timeout 75;
    keepalive_requests 1000;

    # ... server blocks ...
}

Gzip Compression

Gzip compression can significantly reduce the bandwidth required to transfer assets, leading to faster page load times. Ensure it’s enabled and configured appropriately for your content types. The `gzip_comp_level` (1-9) determines the compression ratio; a level of 4-6 is often a good compromise between compression effectiveness and CPU usage.

http {
    # ... other http directives ...

    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 5;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
    gzip_disable "msie6"; # Optional: disable for older IE versions
}

Client Body and Header Buffers

For applications that handle larger file uploads or complex POST requests, tuning `client_body_buffer_size` and `client_max_body_size` is crucial. Similarly, `large_client_header_buffers` can prevent errors when dealing with requests that have many headers.

http {
    # ... other http directives ...

    client_body_buffer_size 128k;
    client_max_body_size 50m; # Adjust as per your application's needs
    large_client_header_buffers 4 128k;
}

Gunicorn Tuning for Python Applications

When deploying Python web applications (e.g., Flask, Django) on OVH, Gunicorn is a popular choice for serving requests. Its performance is heavily influenced by the number of worker processes and their type.

Worker Processes and Type

Gunicorn’s `workers` setting determines the number of worker processes that will handle requests. A common recommendation is `(2 * number_of_cores) + 1`. The `worker_class` is also critical. For I/O-bound applications, `gevent` or `eventlet` (asynchronous workers) can significantly improve concurrency by allowing workers to handle multiple requests simultaneously without blocking. For CPU-bound tasks, the default `sync` worker class might be sufficient, but it’s less efficient for high concurrency.

# Example command to start Gunicorn with gevent workers
gunicorn --workers 4 --worker-class gevent --bind 0.0.0.0:8000 myapp.wsgi:application

If you’re using `gevent` or `eventlet`, ensure you have installed the respective libraries:

pip install gevent

Worker Timeout and Graceful Reloads

The `timeout` setting specifies the number of seconds Gunicorn will wait for a worker to respond before considering it dead. Setting this too low can lead to premature worker restarts for legitimate long-running requests. `graceful_timeout` is used during reloads to allow existing requests to complete.

gunicorn --workers 4 --worker-class gevent --bind 0.0.0.0:8000 --timeout 60 --graceful-timeout 60 myapp.wsgi:application

Access and Error Logging

Proper logging is essential for debugging and performance monitoring. Gunicorn can log to stdout/stderr (useful for containerized environments) or to specific files. Configuring log levels and rotation is important for production systems.

gunicorn --workers 4 --worker-class gevent --bind 0.0.0.0:8000 \
    --access-logfile /var/log/gunicorn/access.log \
    --error-logfile /var/log/gunicorn/error.log \
    --log-level info \
    myapp.wsgi:application

For log rotation, consider using `logrotate` on your OVH instance. A typical `logrotate` configuration for Gunicorn might look like this:

/var/log/gunicorn/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        systemctl reload gunicorn.service > /dev/null 2>&1 || true
    endscript
}

PHP-FPM Tuning for PHP Applications

For PHP applications served via Nginx (e.g., WordPress, Laravel), PHP-FPM is the standard FastCGI Process Manager. Tuning its pool configuration is critical for performance and stability.

Process Manager Settings

PHP-FPM offers three primary process management strategies: `static`, `dynamic`, and `ondemand`. For predictable high-traffic loads, `static` is often preferred as it pre-forks a fixed number of workers, reducing latency. `dynamic` is a good compromise, spawning workers as needed up to a maximum. `ondemand` is resource-efficient but can introduce latency on initial requests.

; /etc/php/8.1/fpm/pool.d/www.conf (example for PHP 8.1)
[www]
user = www-data
group = www-data
listen = /run/php/php8.1-fpm.sock

; Process Management (choose one strategy)
; pm = static
; pm.max_children = 50 ; For static, this is the fixed number of workers

pm = dynamic
pm.max_children = 100 ; Max number of children at any one time
pm.start_servers = 10  ; Number of servers started on boot
pm.min_spare_servers = 5 ; Minimum number of idle servers
pm.max_spare_servers = 20 ; Maximum number of idle servers
; pm = ondemand
; pm.max_children = 50
; pm.process_idle_timeout = 10s

The values for `pm.max_children`, `pm.start_servers`, etc., should be tuned based on your server’s RAM and the typical load. A common starting point for `pm.max_children` is to divide your available RAM (in MB) by the average memory footprint of a PHP-FPM worker process. Monitor your system’s memory usage closely.

Request Termination and Slowlog

To prevent runaway scripts from consuming resources indefinitely, configure `request_terminate_timeout`. The `slowlog` directive is invaluable for identifying performance bottlenecks within your PHP code.

; /etc/php/8.1/fpm/pool.d/www.conf
[www]
; ... other settings ...

request_terminate_timeout = 60s ; Terminate scripts after 60 seconds

; Enable slowlog for debugging
request_slowlog_timeout = 10s
slowlog = /var/log/php/php-fpm-slow.log

Ensure the log directory (`/var/log/php/`) is writable by the PHP-FPM user and that log rotation is configured for these files as well.

Environment Variables and PHP Settings

You can pass environment variables to PHP-FPM processes, which can be useful for application configuration. Additionally, core PHP settings like `memory_limit` and `max_execution_time` can be adjusted within the FPM pool configuration.

; /etc/php/8.1/fpm/pool.d/www.conf
[www]
; ... other settings ...

; Environment variables
env[MY_APP_ENV] = production
env[DATABASE_URL] = postgresql://user:pass@host:port/dbname

; PHP settings
php_admin_value[memory_limit] = 256M
php_admin_value[max_execution_time] = 120
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M

PostgreSQL Performance Tuning on OVH

Optimizing PostgreSQL on OVH involves tuning its configuration parameters, particularly those related to memory, I/O, and query execution. The primary configuration file is `postgresql.conf`.

Shared Buffers and WAL

`shared_buffers` is arguably the most critical parameter. It defines the amount of memory PostgreSQL uses for caching data. A common recommendation is 25% of your server’s total RAM, but this can be adjusted upwards if your server is dedicated to PostgreSQL and has ample RAM. `wal_buffers` is for Write-Ahead Log buffering; a value of 16MB is often sufficient. `wal_writer_delay` controls how often the WAL writer flushes buffers to disk.

# postgresql.conf
shared_buffers = 4GB ; Example for a server with 16GB RAM
wal_buffers = 16MB
wal_writer_delay = 200ms ; Default is 200ms, can be tuned
checkpoint_completion_target = 0.9 ; Spread checkpoints over time
checkpoint_timeout = 5min ; How often checkpoints occur

Work Memory and Maintenance Work Memory

`work_mem` controls the amount of memory used by internal sort operations and hash tables before writing to temporary disk files. It’s allocated per sort/hash operation, so setting it too high can lead to memory exhaustion if many such operations occur concurrently. `maintenance_work_mem` is used for maintenance operations like `VACUUM`, `CREATE INDEX`, and `ALTER TABLE`.

# postgresql.conf
work_mem = 64MB ; Adjust based on query complexity and concurrency
maintenance_work_mem = 512MB ; Can be set higher than work_mem

Effective Cache Size

`effective_cache_size` is a PostgreSQL configuration parameter that informs the query planner about the total amount of memory available for disk caching by the operating system and PostgreSQL’s shared buffers. It doesn’t allocate memory itself but influences the planner’s decision on whether to use index scans. A good starting point is 50-75% of the total system RAM.

# postgresql.conf
effective_cache_size = 12GB ; Example for a server with 16GB RAM

Connection Pooling and Max Connections

Managing database connections efficiently is vital. `max_connections` defines the maximum number of concurrent connections. Ensure this is set high enough to accommodate your application’s needs but not so high that it exhausts server memory. For applications with many short-lived connections, using a connection pooler like PgBouncer is highly recommended. This allows PostgreSQL to maintain a smaller `max_connections` value.

# postgresql.conf
max_connections = 200 ; Adjust based on RAM and application needs
; Consider using PgBouncer for connection pooling

Autovacuum Tuning

Autovacuum is essential for reclaiming space occupied by dead tuples and preventing transaction ID wraparound. Tuning its parameters can significantly impact performance, especially on busy databases.

# postgresql.conf
autovacuum = on
log_autovacuum_min_duration = 1s ; Log autovacuum actions taking longer than 1 second
autovacuum_max_workers = 3 ; Number of autovacuum worker processes
autovacuum_naptime = 15s ; How often to check for jobs
autovacuum_vacuum_threshold = 50 ; Minimum number of rows to vacuum
autovacuum_analyze_threshold = 50 ; Minimum number of rows to analyze
autovacuum_vacuum_scale_factor = 0.1 ; Fraction of table size to vacuum
autovacuum_analyze_scale_factor = 0.05 ; Fraction of table size to analyze

For very large or very active tables, you might need to adjust `autovacuum_vacuum_scale_factor` and `autovacuum_analyze_scale_factor` to lower values or even set them to 0 and rely on `autovacuum_vacuum_threshold` and `autovacuum_analyze_threshold` for specific tables.

Monitoring and Diagnostics

Regular monitoring is key to identifying performance regressions. Use tools like `pg_stat_statements` to find slow queries, `htop` or `top` to monitor resource usage, and Nginx/PHP-FPM logs for application-level issues. For PostgreSQL, `pg_stat_activity` provides real-time information about active queries and connections.

-- Enable pg_stat_statements extension
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- View top 10 slowest queries by total execution time
SELECT
    query,
    calls,
    total_exec_time,
    rows
FROM
    pg_stat_statements
ORDER BY
    total_exec_time DESC
LIMIT 10;

By systematically tuning these components – Nginx for network efficiency, Gunicorn/PHP-FPM for application serving, and PostgreSQL for data management – you can build a robust and high-performing infrastructure on OVH.

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