• 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 Elasticsearch on OVH for Laravel

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and Elasticsearch on OVH for Laravel

Nginx as a High-Performance Frontend for Laravel

When deploying Laravel applications, Nginx serves as an excellent choice for a web server and reverse proxy. Its event-driven, asynchronous architecture makes it highly efficient for handling concurrent connections. For optimal performance, especially with PHP-FPM or Gunicorn, careful configuration is paramount. We’ll focus on tuning Nginx for static file serving, SSL termination, and efficient proxying to your application server.

Core Nginx Configuration for Laravel

The primary configuration file for Nginx is typically located at /etc/nginx/nginx.conf. Within this, we define worker processes and global settings. For a typical OVH VPS, setting worker_processes to the number of CPU cores is a good starting point. worker_connections dictates the maximum number of simultaneous connections a worker can handle; a value of 4096 is common and generally sufficient.

user www-data;
worker_processes auto; # Or set to the number of CPU cores
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;
    server_tokens off; # Important for security

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

    # Gzip compression
    gzip on;
    gzip_disable "msie6";
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    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;

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

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

Laravel Site-Specific Configuration

For each Laravel application, a dedicated server block (virtual host) is recommended. This file is typically placed in /etc/nginx/sites-available/your-app.conf and then symlinked to /etc/nginx/sites-enabled/. Key directives include caching for static assets, proper proxying, and handling of PHP requests.

Static File Caching and Compression

Efficiently serving static assets (CSS, JS, images) is crucial. We’ll leverage browser caching and Nginx’s built-in compression.

server {
    listen 80;
    server_name your-domain.com www.your-domain.com;
    root /var/www/your-app/public; # Adjust to your Laravel project's public directory

    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # Caching for static assets
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|webp|woff|woff2|ttf|eot)$ {
        expires 365d;
        add_header Cache-Control "public, no-transform";
        access_log off; # Optional: reduce log noise for static files
    }

    # PHP-FPM configuration (if using PHP-FPM)
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        # Adjust to your PHP-FPM socket or IP:port
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # Deny access to hidden files
    location ~ /\.ht {
        deny all;
    }

    # Error pages
    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

Tuning Gunicorn for Laravel (with Swoole/Octane) or PHP-FPM

The application server configuration depends on your Laravel deployment strategy. For traditional PHP-FPM, tuning is done within PHP-FPM’s pool configuration. If you’re using Laravel Octane with Swoole or RoadRunner, Gunicorn (or a similar WSGI server) might be involved, or Swoole/RoadRunner directly.

PHP-FPM Tuning

PHP-FPM configuration is typically found in /etc/php/8.1/fpm/pool.d/www.conf (adjust version and pool name as needed). The pm (process manager) setting is critical. dynamic is often a good balance, but ondemand can save resources if traffic is sporadic. For high-traffic sites, static might offer the best raw performance but requires careful memory management.

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

[www]
user = www-data
group = www-data
listen = /var/run/php/php8.1-fpm.sock ; Or a TCP/IP socket like 127.0.0.1:9000
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

; Process Manager settings
pm = dynamic
pm.max_children = 50       ; Max number of children serving requests
pm.start_servers = 5       ; Number of children created at startup
pm.min_spare_servers = 5   ; Min number of idle respawners
pm.max_spare_servers = 10  ; Max number of idle respawners
pm.process_idle_timeout = 10s ; Timeout for idle processes to be killed (ondemand only)
pm.max_requests = 500      ; Max requests per child before respawning

; Other important settings
request_terminate_timeout = 60s ; Timeout for script execution
; memory_limit = 256M ; Adjust based on your application's needs
; post_max_size = 64M
; upload_max_filesize = 64M

Tuning Strategy: Start with dynamic. Monitor server load (CPU, RAM) and PHP-FPM status (using pm.status_path in Nginx). If you see processes constantly being created and destroyed, increase min_spare_servers and max_spare_servers. If memory is an issue, reduce max_children and potentially switch to ondemand. For predictable high load, static with carefully calculated pm.max_children based on available RAM can yield lower latency.

Gunicorn Tuning (for Python-based apps or as a proxy)

If you’re using Gunicorn as a WSGI server for a Python framework (less common for direct Laravel, but possible with tools like python-laravel or for proxying other services), tuning involves worker count and type. For a PHP application served via a WSGI interface (e.g., RoadRunner), Gunicorn might act as an intermediary.

# Example Gunicorn command
gunicorn --workers 4 --worker-class gevent --bind 127.0.0.1:8000 your_app.wsgi:application

Worker Count: A common recommendation is (2 * number_of_cores) + 1. However, this is highly dependent on whether your workers are CPU-bound or I/O-bound. For I/O-bound tasks (like waiting for database or external API calls), asynchronous workers (like gevent or eventlet) are more efficient. For CPU-bound tasks, synchronous workers (like sync or gthread) might be better, but require more processes.

Elasticsearch Performance Tuning on OVH

Elasticsearch is a powerful search engine, but it can be resource-intensive. Proper tuning is essential to avoid performance bottlenecks, especially on shared or VPS environments like OVH.

JVM Heap Size Configuration

The most critical tuning parameter for Elasticsearch is the JVM heap size. It’s configured in /etc/elasticsearch/jvm.options. The rule of thumb is to set the initial (-Xms) and maximum (-Xmx) heap size to 50% of the available system RAM, but **never exceeding 30-31 GB**. Exceeding this threshold can lead to issues with compressed ordinary object pointers (compressed oops).

# /etc/elasticsearch/jvm.options

# Example for a server with 16GB RAM
-Xms8g
-Xmx8g

# Example for a server with 64GB RAM (DO NOT exceed ~31GB for heap)
# -Xms16g
# -Xmx16g

Important: After changing jvm.options, you must restart the Elasticsearch service: sudo systemctl restart elasticsearch.

Filesystem Cache and Swapping

Elasticsearch relies heavily on the operating system’s filesystem cache. Ensure that Elasticsearch is not being swapped out. Add the following to /etc/elasticsearch/elasticsearch.yml to disable swapping for the Elasticsearch process:

# /etc/elasticsearch/elasticsearch.yml
bootstrap.memory_lock: true

You also need to configure the system to allow memory locking. Edit /etc/security/limits.conf:

# /etc/security/limits.conf
elasticsearch   soft    memlock         unlimited
elasticsearch   hard    memlock         unlimited

And ensure the Elasticsearch user is allowed to lock memory. Edit /etc/elasticsearch/jvm.options and uncomment or add:

# /etc/elasticsearch/jvm.options
-XX:-UseCompressedOops

Note: -XX:-UseCompressedOops is generally not recommended unless you are hitting the 32GB limit. If you are not hitting that limit, keep it enabled. The primary goal is to prevent the OS from swapping Elasticsearch’s memory. After these changes, restart Elasticsearch.

Index and Shard Optimization

The number of shards and replicas significantly impacts performance and resource usage. Avoid over-sharding. A common recommendation is to keep shard sizes between 10GB and 50GB. Monitor shard sizes and adjust your indexing strategy accordingly. For Laravel applications, consider using the Elasticsearch Index Lifecycle Management (ILM) feature to automate index management (e.g., rolling over indices based on size or age).

// Example ILM policy (via Elasticsearch API)
PUT _ilm/policy/my_laravel_logs_policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_age": "7d",
            "max_primary_shard_size": "50gb"
          }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

Apply this policy to your index templates. Also, limit the number of replicas to 1 or 0 on development/testing environments to save resources.

Monitoring and Diagnostics

Continuous monitoring is key to identifying and resolving performance issues. Utilize tools like:

  • Nginx: stub_status module for connection counts, access.log and error.log analysis (e.g., using goaccess or ELK stack).
  • PHP-FPM: pm.status_path in Nginx to view active processes, requests, etc.
  • Elasticsearch: Elasticsearch’s own monitoring APIs (_cat APIs, _nodes/stats, _cluster/stats), and tools like Kibana’s Stack Monitoring.
  • System Resources: htop, iotop, vmstat, sar for CPU, memory, I/O, and network usage.

For Laravel-specific issues, enable detailed logging and use tools like Laravel Telescope or the built-in logging to pinpoint slow database queries, external API calls, or inefficient code execution.

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

  • Debugging and Resolving complex WP_DEBUG notice floods issues during heavy concurrent database traffic
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency GitHub API repositories handlers
  • Debugging and Resolving deep-seated hook priority conflicts in third-party Mailchimp Newsletter connectors
  • How to build custom Carbon Fields custom wrappers extensions utilizing modern WordPress Options API schemas
  • WordPress Development Recipe: Staggered database writes for high-volume custom form fields using Filesystem API

Categories

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

Recent Posts

  • Debugging and Resolving complex WP_DEBUG notice floods issues during heavy concurrent database traffic
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency GitHub API repositories handlers
  • Debugging and Resolving deep-seated hook priority conflicts in third-party Mailchimp Newsletter connectors

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • 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