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

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

Nginx as a High-Performance Frontend for Laravel

When deploying Laravel applications, Nginx serves as an indispensable frontend, efficiently handling static assets, SSL termination, and proxying requests to your application server. Proper tuning is critical for maximizing throughput and minimizing latency. We’ll focus on key directives that impact performance.

Core Nginx Configuration Tuning

The primary configuration file, typically located at /etc/nginx/nginx.conf, contains global settings. For production environments, consider the following adjustments:

  • worker_processes: Set this to the number of CPU cores available on your server. This allows Nginx to utilize all available processing power for handling concurrent connections.
  • worker_connections: This defines the maximum number of simultaneous connections that each worker process can handle. A common starting point is 1024 or higher, depending on expected traffic.
  • multi_accept: Setting this to on allows worker processes to accept as many new connections as possible at once, rather than one at a time.
  • keepalive_timeout: This directive specifies the time a persistent connection will remain open. A value between 60 and 120 seconds is often a good balance between resource usage and client responsiveness.
  • sendfile: Set to on to enable efficient file transfer from the kernel’s page cache, reducing data copying between user space and kernel space.
  • tcp_nopush: Set to on to enable Nginx to send header information with the file data in one packet, reducing the number of packets sent.
  • tcp_nodelay: Set to on to disable the Nagle algorithm, which can reduce latency for real-time applications by sending small packets immediately.

Here’s an example snippet for nginx.conf:

worker_processes auto;
worker_connections 4096;
multi_accept on;

events {
    worker_connections 4096;
}

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

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;

    keepalive_timeout  120;
    keepalive_requests 1000;

    # Gzip compression for text-based assets
    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;

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

    # ... other http configurations ...
}

Laravel Application Server Configuration (Gunicorn/PHP-FPM)

The choice between Gunicorn (for Python-based frameworks like Flask/Django, but often used with PHP via tools like php-fpm) and PHP-FPM (for PHP applications like Laravel) dictates the specific tuning parameters. We’ll cover PHP-FPM as it’s the standard for Laravel.

PHP-FPM Tuning

PHP-FPM’s configuration, typically found in /etc/php/[version]/fpm/php-fpm.conf and /etc/php/[version]/fpm/pool.d/www.conf, is crucial for managing PHP processes. The pm (process manager) setting is key.

  • pm: Options include static, dynamic, and ondemand.
    • static: Pre-forks a fixed number of child processes. Good for predictable loads, but can be wasteful if idle.
    • dynamic: Starts with a few processes and spawns more as needed, up to a `pm.max_children` limit. It also kills idle processes. This is generally the recommended setting for most web applications.
    • ondemand: Spawns processes only when a request comes in and kills them after a timeout. Can save resources but might introduce slight latency on the first request.
  • pm.max_children: The maximum number of child processes that can be alive at the same time. This should be carefully tuned based on your server’s RAM. A common formula is (Total RAM - Web Server RAM) / Average Process Size.
  • pm.start_servers: The number of child processes to start when PHP-FPM starts.
  • pm.min_spare_servers: The minimum number of idle (spare) processes that should be kept active.
  • pm.max_spare_servers: The maximum number of idle (spare) processes that should be kept active.
  • request_terminate_timeout: The maximum time a script is allowed to run before being terminated. Crucial for preventing runaway scripts.
  • pm.process_idle_timeout: The number of seconds after which an idle process will be killed. (Only applicable for dynamic and ondemand).

Example www.conf for a DigitalOcean droplet with 4GB RAM (adjust `pm.max_children` based on your actual RAM and typical PHP process memory footprint):

; /etc/php/8.1/fpm/pool.d/www.conf (example for PHP 8.1)
; Adjust based on your server's RAM and typical PHP process memory usage.
; For 4GB RAM, a starting point for pm.max_children might be around 100-150.
; Monitor memory usage closely.

[www]
user = www-data
group = www-data
listen = /run/php/php8.1-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 150
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.process_idle_timeout = 10s

; Maximum execution time of scripts.
max_execution_time = 60

; Maximum amount of time each script may spend decoding input data.
max_input_time = 60

; Maximum amount of memory a script may consume (e.g. 256MB).
memory_limit = 256M

; Request terminate timeout.
request_terminate_timeout = 120

; Set to 'no' for production to avoid potential issues with error reporting.
; display_errors = off

; Log errors to stderr.
; log_level = notice
; error_log = /var/log/php/php8.1-fpm.log

And the corresponding Nginx configuration to proxy requests to PHP-FPM:

server {
    listen 80;
    server_name your_domain.com www.your_domain.com;
    root /var/www/your_laravel_app/public;

    index index.php index.html index.htm;

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

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        # Make sure this matches your PHP-FPM pool configuration
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

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

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

Redis for Caching and Session Management

Redis is a powerful in-memory data structure store, commonly used for caching, session storage, and message brokering in Laravel applications. Optimizing Redis performance involves both server-side configuration and client-side usage patterns.

Redis Server Configuration (`redis.conf`)

The main Redis configuration file is typically located at /etc/redis/redis.conf. Key parameters for performance include:

  • maxmemory: Set a limit on the amount of memory Redis can use. This prevents Redis from consuming all available RAM and causing system instability. Choose a value that leaves sufficient memory for the OS and other services.
  • maxmemory-policy: Defines how Redis evicts keys when maxmemory is reached. For caching, allkeys-lru (Least Recently Used) or volatile-lru (for keys with an expire set) are common choices.
  • save directives: These control RDB snapshotting. For high-traffic, volatile caches, you might disable or significantly reduce the frequency of RDB saves to avoid I/O overhead. If persistence is critical, tune these carefully.
  • appendonly: If persistence is required, enabling AOF (Append Only File) can provide better durability than RDB. Tune appendfsync (e.g., everysec) to balance durability and performance.
  • tcp-backlog: The maximum number of pending connections. Increasing this can help during sudden traffic spikes.

Example snippet for redis.conf on a DigitalOcean droplet:

# /etc/redis/redis.conf

# Set a memory limit (e.g., 2GB for a server with 4GB RAM, leaving room for OS and app)
maxmemory 2gb

# Eviction policy for when maxmemory is reached. LRU is good for caches.
maxmemory-policy allkeys-lru

# Disable RDB snapshots if Redis is purely for volatile cache and AOF is enabled
# save ""

# Enable AOF persistence for better durability if needed
# appendonly yes
# appendfsync everysec ; Balance between durability and performance

# Increase TCP backlog for handling more concurrent connections
tcp-backlog 511

# Bind to a specific IP or localhost if only accessed locally
# bind 127.0.0.1 ::1
# Or bind to a private IP if accessed from other droplets in the same VPC
# bind 10.10.0.5

# Require a password for security
# requirepass your_strong_redis_password

After modifying redis.conf, restart the Redis service:

sudo systemctl restart redis-server

Laravel Redis Configuration (`.env`)

Ensure your Laravel application’s .env file is correctly configured to connect to your Redis instance. For production, it’s highly recommended to use a password and bind Redis to a private IP or localhost.

# .env file in your Laravel project

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=your_strong_redis_password
REDIS_PORT=6379

# If Redis is on a different server or accessed via private IP
# REDIS_HOST=10.10.0.5
# REDIS_PASSWORD=your_strong_redis_password
# REDIS_PORT=6379

Monitoring and Iterative Tuning

Performance tuning is an ongoing process. Regularly monitor your system’s resource utilization and application performance metrics. Key tools and metrics include:

  • Nginx: Use tools like nginx-status module or Prometheus exporters to monitor active connections, requests per second, and error rates.
  • PHP-FPM: Monitor the number of active, idle, and busy processes via the FPM status page (requires enabling pm.status_path in www.conf and configuring Nginx to access it).
  • Redis: Use redis-cli INFO to check memory usage, connected clients, cache hit rates (if applicable), and command latency.
  • System Resources: Monitor CPU usage, RAM usage, disk I/O, and network traffic using tools like htop, vmstat, iostat, and DigitalOcean’s monitoring dashboard.
  • Application Performance Monitoring (APM): Tools like New Relic, Datadog, or Sentry can provide deep insights into application bottlenecks, slow database queries, and external service calls.

Start with conservative settings and gradually increase limits (e.g., pm.max_children, worker_connections) while observing resource usage. If you encounter out-of-memory errors, scale back the limits. If your application is consistently CPU-bound, consider optimizing your code or scaling up your droplet.

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

  • Disaster Recovery 101: Architecting Auto-Failovers for Redis and PHP Deployments on OVH
  • How We Audited a High-Traffic WooCommerce Enterprise Stack on Google Cloud and Mitigated Race conditions during high-concurrency payment processing
  • Disaster Recovery 101: Architecting Auto-Failovers for Elasticsearch and Magento 2 Deployments on DigitalOcean
  • An Auditor’s Checklist for Securing WordPress Backends on OVH
  • Step-by-Step: Diagnosing Perl script high CPU throttling due to unoptimized regular expressions on AWS Servers

Copyright © 2026 · Vinay Vengala