• 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 OVH for Python

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and MySQL on OVH for Python

Nginx Configuration for High-Traffic Python Applications

Optimizing Nginx is paramount for serving Python web applications efficiently, especially when dealing with high concurrency. We’ll focus on key directives that impact performance and stability, assuming a standard setup with Gunicorn or PHP-FPM.

Worker Processes and Connections

The number of worker processes directly influences how many requests Nginx can handle concurrently. A common starting point is to set worker_processes to the number of CPU cores available. worker_connections dictates the maximum number of simultaneous connections a single worker process can handle.

On OVH, where you might have dedicated or VPS instances, identifying the core count is straightforward. For a Linux system, you can use nproc or check /proc/cpuinfo.

Here’s a snippet from nginx.conf:

user www-data;
worker_processes auto; # Or set to the number of CPU cores, e.g., worker_processes 4;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 4096; # Adjust based on expected load and system limits
    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;

    # ... other http configurations
}

multi_accept on; allows workers to accept multiple connections at once, further improving concurrency.

Buffering and Timeouts

Nginx buffering can significantly impact performance. While buffering can help smooth out traffic spikes, overly aggressive buffering can increase latency. For API-heavy applications, you might want to disable or tune down client request buffering.

client_body_buffer_size, client_header_buffer_size, and large_client_header_buffers are key. For typical web requests, default values are often sufficient. However, for large file uploads, you might need to increase client_body_buffer_size.

Timeouts are crucial for preventing hung connections from consuming resources. client_header_timeout, client_body_timeout, and send_timeout should be set to reasonable values, typically between 30-60 seconds, depending on the application’s expected response times.

http {
    # ... other http configurations

    client_body_buffer_size 128k;
    client_header_buffer_size 128k;
    large_client_header_buffers 4 128k;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    client_header_timeout 10s;
    client_body_timeout 10s;
    send_timeout 10s;

    # ... other http configurations
}

Gzip Compression

Enabling Gzip compression can drastically reduce the amount of data transferred, leading to faster page loads. It’s a low-CPU cost operation for modern servers.

http {
    # ... other http configurations

    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6; # Compression level (1-9)
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    # ... other http configurations
}

Proxying to Gunicorn/PHP-FPM

When proxying to your Python application server (Gunicorn) or PHP-FPM, ensure the proxy settings are optimized. For Gunicorn, this typically involves HTTP or FastCGI (if using a WSGI server that supports it). For PHP-FPM, it’s FastCGI.

Key directives include proxy_connect_timeout, proxy_send_timeout, and proxy_read_timeout. These should generally be longer than the client timeouts to allow the backend to process requests.

server {
    listen 80;
    server_name your_domain.com;

    location / {
        proxy_pass http://unix:/path/to/your/app.sock; # Or http://127.0.0.1:8000;
        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_connect_timeout 75s;
        proxy_send_timeout 75s;
        proxy_read_timeout 75s;
    }

    # For PHP-FPM
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; # Adjust PHP version and path
        fastcgi_read_timeout 300; # Longer timeout for potentially long PHP scripts
    }
}

Gunicorn Tuning for Python Web Applications

Gunicorn (Green Unicorn) is a popular WSGI HTTP Server for Python. Its performance is heavily influenced by the number of worker processes and the type of worker class used.

Worker Processes and Threads

The --workers flag determines the number of worker processes. A common recommendation is (2 * number_of_cores) + 1. This formula aims to keep CPU cores busy while accounting for I/O waits.

Gunicorn also supports threads via the --threads flag. Using threads can be beneficial for I/O-bound applications, as they allow a single worker process to handle multiple requests concurrently without the overhead of creating new processes. However, due to Python’s Global Interpreter Lock (GIL), threads don’t offer true parallelism for CPU-bound tasks.

For CPU-bound applications, relying solely on multiple worker processes is generally more effective. For I/O-bound applications (e.g., those making many external API calls or database queries), a combination of workers and threads can be optimal.

# Example Gunicorn command
gunicorn --workers 3 --threads 2 --bind unix:/path/to/your/app.sock myapp.wsgi:application

In this example, we have 3 worker processes, and each worker can handle up to 2 threads. This configuration would be suitable for a server with 1-2 CPU cores, balancing CPU and I/O.

Worker Classes

Gunicorn offers different worker classes:

  • sync: The default worker class. It’s simple but can block under heavy load if requests are slow.
  • eventlet: Uses eventlet for non-blocking I/O. Good for I/O-bound applications.
  • gevent: Uses gevent for non-blocking I/O. Similar to eventlet, often preferred for its performance.
  • tornado: Uses the Tornado IOLoop.
  • asyncio: Supports Python’s asyncio framework.

For most modern Python applications, especially those with significant I/O, using gevent or eventlet is highly recommended. You’ll need to install them (e.g., pip install gevent).

# Example using gevent worker
gunicorn --worker-class gevent --workers 3 --threads 2 --bind unix:/path/to/your/app.sock myapp.wsgi:application

Timeouts and Keepalive

--timeout specifies the number of seconds to wait for a worker to respond. If a worker takes longer, it’s killed and restarted. This prevents hung requests from blocking workers indefinitely. Set this based on your application’s longest expected request processing time.

--keep-alive controls the number of requests a worker can handle before being restarted. Setting this to a reasonable number (e.g., 1000) can reduce overhead by reusing connections.

gunicorn --workers 4 --threads 2 --timeout 120 --keep-alive 1000 --bind unix:/path/to/your/app.sock myapp.wsgi:application

PHP-FPM Tuning for PHP Applications

For PHP applications, PHP-FPM (FastCGI Process Manager) is the standard. Its configuration heavily impacts how PHP scripts are executed and how many requests can be handled.

Process Management Modes

PHP-FPM offers three process management modes, configured in php-fpm.conf or pool configuration files (e.g., www.conf):

  • static: Pre-spawns a fixed number of child processes. Good for predictable loads.
  • dynamic: Spawns processes as needed, up to a defined maximum. More flexible.
  • ondemand: Spawns processes only when a request arrives and kills them after a period of inactivity. Saves resources but can increase latency for the first request.

For high-traffic sites, dynamic or static are generally preferred. dynamic offers a good balance.

; /etc/php/7.4/fpm/pool.d/www.conf
[www]
user = www-data
group = www-data
listen = /var/run/php/php7.4-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 50      ; Maximum number of children that can be started.
pm.min_spare_servers = 5  ; Minimum number of servers to keep idle.
pm.max_spare_servers = 10 ; Maximum number of servers to keep idle.
pm.process_idle_timeout = 10s; The number of seconds after which an idle process will be killed.
pm.max_requests = 500     ; Maximum number of requests each child process should serve.

In this example, using dynamic mode:

  • pm.max_children: The absolute maximum number of PHP processes that can run simultaneously. This is the most critical setting and should be tuned based on your server’s RAM. Too high, and you’ll OOM kill.
  • pm.min_spare_servers and pm.max_spare_servers: These define the range of idle processes PHP-FPM tries to maintain.
  • pm.process_idle_timeout: How long an idle process waits before being terminated.
  • pm.max_requests: Limits the number of requests a child process can handle before it’s recycled. This helps prevent memory leaks from accumulating over time.

Resource Limits and Timeouts

request_terminate_timeout in the pool configuration is analogous to Gunicorn’s --timeout. It defines how long a single PHP script can run before being terminated. This is crucial for preventing runaway scripts.

max_execution_time in php.ini is also relevant, but request_terminate_timeout in FPM often takes precedence for FastCGI requests.

; /etc/php/7.4/fpm/pool.d/www.conf
[www]
# ... other settings
request_terminate_timeout = 120s ; Timeout for a single script execution

MySQL Tuning for Performance

Database performance is often the bottleneck. Tuning MySQL involves adjusting buffer sizes, query cache settings, and connection handling.

Key Configuration Variables

These variables are set in my.cnf or mysqld.cnf. Always restart the MySQL service after changes.

[mysqld]
# General Settings
datadir=/var/lib/mysql
socket=/var/run/mysqld/mysqld.sock
pid-file=/var/run/mysqld/mysqld.pid

# Performance Tuning
innodb_buffer_pool_size = 512M  # Crucial for InnoDB performance. Adjust based on available RAM (e.g., 50-70% of RAM if dedicated DB server)
innodb_log_file_size = 256M     # Size of InnoDB redo log files. Larger can improve write performance but increases recovery time.
innodb_flush_log_at_trx_commit = 1 # 1=ACID compliant (safest, slowest), 0=faster, less safe, 2=balance. For high write loads, consider 2.
innodb_flush_method = O_DIRECT  # Bypass OS cache for InnoDB data files. Can improve performance on systems with sufficient RAM.

# Connection Handling
max_connections = 200           # Maximum number of simultaneous client connections. Adjust based on application needs and server resources.
thread_cache_size = 16          # Number of threads the server should cache.
table_open_cache = 2000         # Number of tables the server keeps open.
table_definition_cache = 1000   # Number of table definitions the server keeps in cache.

# Query Cache (Deprecated in MySQL 5.7, removed in 8.0. Consider application-level caching instead)
# query_cache_type = 1
# query_cache_size = 64M

Important Notes:

  • innodb_buffer_pool_size is the single most important setting for InnoDB. Ensure it’s not so large that it causes swapping.
  • innodb_flush_log_at_trx_commit: Setting to 2 can significantly boost write performance at the cost of potentially losing the last second of transactions during an OS crash (not a MySQL crash).
  • Query Cache: If you are on MySQL 8.0+, the query cache is removed. For older versions, it can sometimes cause contention. Test thoroughly before enabling.

Slow Query Log Analysis

Identifying slow queries is critical. Enable the slow query log to capture queries that exceed a defined execution time.

[mysqld]
# ... other settings
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 # Log queries that don't use indexes

Regularly analyze the mysql-slow.log file using tools like mysqldumpslow or pt-query-digest (from Percona Toolkit) to pinpoint problematic queries. Optimize these queries with appropriate indexing.

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

# Example using pt-query-digest
pt-query-digest /var/log/mysql/mysql-slow.log > /tmp/slow_query_report.txt

Monitoring and Iteration

Tuning is an iterative process. Continuous monitoring is key to understanding the impact of your changes and identifying new bottlenecks.

Key Metrics to Monitor

  • Nginx: Active connections, requests per second, error rates (4xx, 5xx), worker connections, buffer usage. Tools like nginx-module-vts are invaluable.
  • Gunicorn/PHP-FPM: Worker utilization, request latency, error rates, memory usage per worker.
  • MySQL: Connections, query throughput, slow queries, buffer pool hit rate, disk I/O, CPU usage, memory usage.
  • System: CPU load, memory usage, disk I/O, network traffic.

Utilize tools like Prometheus with Grafana, Datadog, New Relic, or even basic system tools like htop, iotop, and netstat to gather this data.

Tuning Workflow

  • Baseline: Measure current performance under typical and peak loads.
  • Identify Bottleneck: Use monitoring tools to find the slowest component (Nginx, App Server, DB, or System Resource).
  • Tune One Component: Make small, incremental changes to the identified component.
  • Measure Again: Observe the impact of the change. Did it improve performance? Did it shift the bottleneck elsewhere?
  • Repeat: Continue the cycle until performance targets are met or no further significant improvements can be made.

Remember to test changes in a staging environment that closely mirrors your production setup before deploying them live.

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

  • Orchestrating Serverless PHP 9 with AWS Lambda and API Gateway: A Deep Dive into Performance and Cost Optimization
  • Leveraging PHP 8.3 JIT and Vectorization for Extreme Performance in Laravel Applications
  • Leveraging PHP 9’s JIT Compiler and Concurrent Execution for High-Performance Laravel Microservices
  • Leveraging PHP 8.3 JIT and Vectorization for High-Throughput Microservices in a Laravel Ecosystem
  • Leveraging Laravel Octane and Docker Swarm for High-Performance, Scalable WordPress Headless Deployments

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (664)
  • Desktop Applications (14)
  • DevOps (11)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (6)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (873)
  • PHP (19)
  • PHP Development (49)
  • Plugins & Themes (244)
  • Programming Languages (10)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (650)
  • SEO & Growth (492)
  • Server (118)
  • Softwares (1)
  • Ubuntu (9)
  • Uncategorized (24)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (26)
  • WordPress Plugin Development (728)
  • WordPress Theme Development (357)

Recent Posts

  • Orchestrating Serverless PHP 9 with AWS Lambda and API Gateway: A Deep Dive into Performance and Cost Optimization
  • Leveraging PHP 8.3 JIT and Vectorization for Extreme Performance in Laravel Applications
  • Leveraging PHP 9's JIT Compiler and Concurrent Execution for High-Performance Laravel Microservices

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • WordPress Plugin Development (728)
  • Debugging & Troubleshooting (664)
  • Security & Compliance (650)
  • SEO & Growth (492)

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