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

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

Optimizing Nginx for Laravel on OVH: A Deep Dive

This section focuses on fine-tuning Nginx as the front-facing web server for your Laravel application hosted on OVH. We’ll cover essential directives for performance, security, and efficient resource utilization.

Nginx Configuration for Laravel

A robust Nginx configuration is paramount. We’ll start with a typical setup and then introduce specific optimizations.

Core Server Block Structure

This is a foundational server block. Pay close attention to the try_files directive, which is crucial for Laravel’s routing.

server {
    listen 80;
    listen [::]:80;
    server_name your_domain.com www.your_domain.com;
    root /var/www/your_laravel_app/public; # Adjust to your Laravel public directory

    index index.php index.html index.htm;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ /index.php?$query_string;
    }

    # Pass PHP scripts to the PHP-FPM listener
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        # With php-fpm:
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; # Adjust PHP version and socket path
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # Deny access to .htaccess files, if any
    location ~ /\.ht {
        deny all;
    }

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

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

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

Tuning Nginx Worker Processes and Connections

The worker_processes and worker_connections directives are critical for handling concurrent requests. On OVH servers, especially VPS instances, you’ll want to tune these based on your CPU cores and available RAM.

To determine the optimal number of worker processes, a good starting point is to match the number of CPU cores available to your instance. You can check this with nproc.

nproc

Then, set worker_processes in /etc/nginx/nginx.conf. For a 4-core VPS, worker_processes 4; is a reasonable starting point. For hyper-threaded CPUs, you might experiment with auto.

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

events {
    worker_connections 1024; # Adjust based on RAM and expected load
    # multi_accept on; # Can improve performance on busy servers
}

worker_connections defines the maximum number of simultaneous connections that each worker process can handle. The total maximum connections will be worker_processes * worker_connections. A common value is 1024, but this can be increased if you have ample RAM and expect high concurrency. Monitor your server’s load and connection counts to find the sweet spot.

Enabling HTTP/2

HTTP/2 offers significant performance improvements over HTTP/1.1. Ensure your OVH SSL certificate is correctly configured and then enable HTTP/2 in your server block.

server {
    listen 443 ssl http2; # Enable SSL and HTTP/2
    listen [::]:443 ssl http2;
    server_name your_domain.com www.your_domain.com;

    ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem; # Adjust path
    ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem; # Adjust path

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

Gunicorn/PHP-FPM Tuning for Laravel

The choice between Gunicorn (for Python-based frameworks) and PHP-FPM (for PHP) dictates the tuning strategy. Since the prompt mentions Laravel, we’ll focus on PHP-FPM. If you were using a Python framework like Django or Flask, Gunicorn tuning would be the focus.

PHP-FPM Configuration Tuning

PHP-FPM (FastCGI Process Manager) is the de facto standard for running PHP applications with Nginx. Its configuration significantly impacts application responsiveness and resource usage.

The primary configuration file is typically located at /etc/php/8.1/fpm/php-fpm.conf (adjust version as needed). Within this file, the pool.d/www.conf (or a similarly named file in pool.d/) is where most tuning occurs.

Process Manager Settings

The pm directive controls how PHP-FPM manages worker processes. The most common and recommended settings for production are dynamic or ondemand.

  • pm = dynamic: This is a good balance. PHP-FPM starts a few children, and if the load increases, it spawns more up to pm.max_children. When idle, it can kill off excess children down to pm.min_spare_servers.
  • pm = ondemand: Processes are only created when a request comes in. This can save memory when idle but might introduce slight latency on the first request after an idle period.
  • pm = static: A fixed number of children are always kept running. This offers the most consistent performance but can be wasteful of resources if the load is highly variable.

For a typical Laravel application on OVH, dynamic is often the best starting point. Let’s configure it:

; In /etc/php/8.1/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 50       ; Adjust based on RAM and Nginx worker_connections
pm.min_spare_servers = 5   ; Minimum idle servers
pm.max_spare_servers = 10  ; Maximum idle servers
pm.start_servers = 2       ; Initial number of servers to start
pm.process_idle_timeout = 10s; For pm = dynamic, idle timeout before killing
pm.max_requests = 500      ; Restart processes after this many requests to prevent memory leaks

Tuning pm.max_children: This is the most critical setting. It should be calculated based on your server’s RAM and the memory footprint of your Laravel application. A common formula is: (Total RAM - Reserved RAM for OS/Nginx/Redis) / Average PHP process memory. Start conservatively and monitor memory usage. If you see OOM killer events or high swap usage, reduce this value. If your server has plenty of free RAM and is still struggling, you might increase it.

Tuning pm.max_requests: Setting this to a reasonable number (e.g., 500-1000) helps prevent memory leaks from accumulating over time by periodically restarting worker processes. This is a good practice for long-running applications.

Tuning Request Handling and Timeout

The request_terminate_timeout and request_slowlog_timeout are important for managing long-running requests and identifying performance bottlenecks.

; In /etc/php/8.1/fpm/pool.d/www.conf
request_terminate_timeout = 60s ; Terminate scripts exceeding this time
request_slowlog_timeout = 10s   ; Log scripts exceeding this time to slowlog file
slowlog = /var/log/php/php8.1-fpm.slow.log ; Path to slow log file

Ensure the directory for slowlog exists and is writable by the PHP-FPM user (e.g., www-data).

sudo mkdir -p /var/log/php
sudo chown www-data:www-data /var/log/php

Restarting PHP-FPM

After making changes to PHP-FPM configuration, always restart the service:

sudo systemctl restart php8.1-fpm # Adjust PHP version as needed

Redis Performance Tuning on OVH

Redis is an excellent choice for caching in Laravel, significantly reducing database load. Proper configuration is key to its effectiveness.

Redis Configuration for Caching

The main configuration file is /etc/redis/redis.conf. We’ll focus on directives relevant to caching performance and stability.

Memory Management

maxmemory is crucial to prevent Redis from consuming all available RAM. Set this to a value that leaves enough memory for your OS, Nginx, and PHP-FPM. A common strategy is to allocate a fixed portion of your server’s RAM.

# In /etc/redis/redis.conf
maxmemory 512mb       # Example: Allocate 512MB for Redis
maxmemory-policy allkeys-lru # Evict least recently used keys when maxmemory is reached

The maxmemory-policy determines how Redis evicts keys when the memory limit is reached. allkeys-lru (Least Recently Used) is generally a good default for caching, as it removes the least recently accessed items first.

Persistence (RDB vs. AOF)

For a cache-only Redis instance, persistence can often be disabled or configured minimally to reduce I/O overhead and improve performance. If Redis is also used for critical data that must not be lost, persistence is essential.

To disable RDB snapshots:

# In /etc/redis/redis.conf
save "" # Disable RDB snapshots

If you need persistence, consider AOF (Append Only File) with appendfsync everysec for a balance between durability and performance. For pure caching, disabling persistence is often preferred.

Network and Client Settings

Binding Redis to a specific IP address enhances security. If Redis is only accessed by your web server on the same machine, binding to 127.0.0.1 is recommended.

# In /etc/redis/redis.conf
bind 127.0.0.1 # Or your server's private IP if accessed from other internal servers

tcp-backlog can be increased to handle a higher volume of incoming connections, especially under heavy load.

# In /etc/redis/redis.conf
tcp-backlog 511 # Default is 511, can be increased if needed, e.g., 1024

Restarting Redis

Apply configuration changes by restarting the Redis service:

sudo systemctl restart redis-server

Monitoring and Diagnostics

Continuous monitoring is essential to validate your tuning efforts and identify new bottlenecks. Use a combination of system tools and application-level metrics.

Nginx Monitoring

Check Nginx status and error logs:

sudo systemctl status nginx
sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/nginx/access.log

Use nginx -t to test configuration syntax before reloading.

sudo nginx -t
sudo systemctl reload nginx

PHP-FPM Monitoring

Check PHP-FPM status and slow logs:

sudo systemctl status php8.1-fpm # Adjust PHP version
sudo tail -f /var/log/php/php8.1-fpm.log # General logs
sudo tail -f /var/log/php/php8.1-fpm.slow.log # Slow script logs

You can also use php-fpm -t to test configuration syntax.

sudo php-fpm8.1 -t # Adjust PHP version
sudo systemctl reload php8.1-fpm # Adjust PHP version

Redis Monitoring

Use the Redis CLI for real-time insights:

redis-cli
127.0.0.1:6379> INFO memory
127.0.0.1:6379> INFO stats
127.0.0.1:6379> INFO persistence
127.0.0.1:6379> MONITOR # Use with caution, can be very verbose

INFO memory will show your current memory usage and maxmemory. INFO stats provides hit/miss ratios for the cache, crucial for evaluating Redis’s effectiveness.

Check Redis logs for errors:

sudo tail -f /var/log/redis/redis-server.log

Laravel Application-Level Optimizations

While infrastructure tuning is vital, application-level optimizations in Laravel are equally important. Ensure you’re leveraging these:

  • Caching: Use Redis for route, view, configuration, and application caching.
  • Database Query Optimization: Eager load relationships (with()), use select() to fetch only necessary columns, and optimize SQL queries.
  • Queueing Jobs: Offload time-consuming tasks (email sending, image processing) to background queues (e.g., using Redis as a queue driver).
  • Opcode Caching: Ensure OPcache is enabled and configured correctly for PHP.
  • Asset Bundling: Use Laravel Mix or Vite to compile and minify CSS/JS assets.

By combining robust infrastructure tuning with smart application development practices, you can achieve a highly performant and scalable Laravel application 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