• 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 MongoDB on AWS for Laravel

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and MongoDB on AWS for Laravel

Nginx as a High-Performance Frontend for Laravel Applications

When deploying Laravel applications, Nginx serves as an excellent choice for a web server due to its event-driven architecture, making it highly efficient at handling concurrent connections. For PHP-based Laravel apps, we’ll typically pair Nginx with PHP-FPM. For Python-based applications using Gunicorn, Nginx acts as a reverse proxy.

Nginx Configuration for PHP-FPM (Laravel)

The core of Nginx configuration for PHP involves setting up a location block to handle PHP requests and pass them to the PHP-FPM process. This configuration assumes a standard PHP-FPM setup listening on a Unix socket or a TCP port.

Optimizing PHP-FPM Worker Processes

Tuning PHP-FPM’s process management is critical. The pm (process manager) setting dictates how workers are managed. For production, dynamic or ondemand are generally preferred over static to conserve resources when idle, but static can offer lower latency under consistent high load. Adjust pm.max_children, pm.start_servers, pm.min_spare_servers, and pm.max_spare_servers based on your server’s CPU and RAM. A common starting point for a moderately sized EC2 instance (e.g., m5.large) might be:

PHP-FPM Pool Configuration Example (/etc/php/8.1/fpm/pool.d/www.conf)

; Choose how the process manager (pm) should take on the task of managing the processes.
; Available options:
;   'static' - the number of child processes is never changed.
;   'dynamic' - the number of child processes is adjusted dynamically.
;   'ondemand' - the number of child processes is adjusted on demand.
pm = dynamic

; The default value is 'dynamic', and it depends on pm.max_children,
; pm.start_servers, pm.min_spare_servers and pm.max_spare_servers.
; If pm is set to 'static', this value is used directly.
; Default value: 0
pm.max_children = 100

; These values are used when pm is set to 'dynamic', and are ignored when 'static' or 'ondemand'.
; Number of child processes to start when pm is set to 'dynamic' or 'ondemand'.
; Default value: 5
pm.start_servers = 10

; Minimum number of child processes which should be kept active.
; Default value: 2
pm.min_spare_servers = 5

; Maximum number of child processes which should be kept active.
; Default value: 5
pm.max_spare_servers = 20

; The maximum number of requests each child process should execute before re-spawning.
; This can be useful to prevent memory leaks from accumulating over time.
; Default value: 0 (unlimited)
pm.max_requests = 500

; The TCP socket or the pipe address on which to listen.
; Default value: /run/php/php8.1-fpm.sock
; listen = /run/php/php8.1-fpm.sock

; If you are listening on a TCP socket, you can specify the address and port.
; listen = 127.0.0.1:9000

Nginx Server Block for Laravel

The Nginx server block should be configured to serve static assets directly and pass PHP requests to the PHP-FPM pool. Ensure fastcgi_read_timeout is set appropriately for long-running Laravel tasks (e.g., queue workers processing). A common value is 300 seconds, but this can be increased if necessary.

Nginx Site Configuration Example (/etc/nginx/sites-available/your_laravel_app)

server {
    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 / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # Pass PHP scripts to FastCGI server
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        # With php-fpm listening on a Unix socket:
        fastcgi_pass unix:/run/php/php8.1-fpm.sock; # Adjust to your PHP-FPM socket path
        # With php-fpm listening on TCP/IP:
        # fastcgi_pass 127.0.0.1:9000;

        # Increase timeout for long-running scripts
        fastcgi_read_timeout 300s; # 5 minutes
    }

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

    # Serve static files directly
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public";
        access_log off;
    }

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

Gunicorn Configuration for Python/Laravel (if applicable)

If your Laravel application is part of a larger Python ecosystem or uses Python for specific services, Gunicorn is a common WSGI HTTP Server. Nginx will act as a reverse proxy, forwarding requests to Gunicorn. The key is to configure Gunicorn’s worker processes and bind address.

Gunicorn Worker Processes and Threads

Gunicorn’s worker class and number of workers significantly impact performance. The sync worker class is the default and most basic. For I/O-bound applications, the gevent or eventlet worker classes (which use asynchronous I/O) can offer better concurrency. The number of workers is typically set to (2 * number_of_cores) + 1 as a starting point. Threads can be used with the gthread worker class.

Gunicorn Command Line Example

gunicorn --workers 4 --threads 2 --worker-class gevent --bind 0.0.0.0:8000 your_app.wsgi:application

In this example:

  • --workers 4: Sets 4 worker processes.
  • --threads 2: Sets 2 threads per worker (if using gthread worker class).
  • --worker-class gevent: Uses the gevent worker class for asynchronous I/O.
  • --bind 0.0.0.0:8000: Binds Gunicorn to all network interfaces on port 8000.
  • your_app.wsgi:application: Points to your application’s WSGI entry point.

Nginx Configuration for Gunicorn

Nginx will proxy requests to the Gunicorn server. It’s crucial to set appropriate timeouts and buffer sizes to prevent issues with long-running requests.

Nginx Server Block for Gunicorn

server {
    listen 80;
    server_name your_python_app.com www.your_python_app.com;

    location / {
        proxy_pass http://127.0.0.1:8000; # Matches Gunicorn's bind address
        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;

        # Timeouts for proxying
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s; # Crucial for long-running requests
    }

    # Serve static files directly if Gunicorn is not handling them
    location /static/ {
        alias /path/to/your/app/static/; # Adjust to your static files directory
        expires 1y;
        add_header Cache-Control "public";
    }

    location /media/ {
        alias /path/to/your/app/media/; # Adjust to your media files directory
        expires 1y;
        add_header Cache-Control "public";
    }
}

MongoDB Tuning for Laravel Applications

Optimizing MongoDB involves several aspects, from schema design to server configuration. For Laravel applications, common bottlenecks include inefficient queries, lack of proper indexing, and insufficient server resources.

Indexing Strategies

The most impactful optimization is proper indexing. Analyze your application’s queries using MongoDB’s explain() method. Identify slow queries in the MongoDB logs (if enabled) or via monitoring tools.

Identifying Slow Queries

# Connect to MongoDB
mongo

# Enable slow query logging (e.g., log queries slower than 100ms)
db.setProfilingLevel(1, 100)

# Perform application actions that trigger slow queries

# View slow queries
db.system.profile.find().pretty()

# Disable profiling when done
db.setProfilingLevel(0)

Once slow queries are identified, create appropriate indexes. For example, if you frequently query a `users` collection by `email` and `status`:

Creating Compound Indexes

db.users.createIndex( { email: 1, status: 1 } )

Consider the order of fields in compound indexes based on query selectivity and sort order. Use tools like mongodbatlas.com/tools/db-schema-advisor for schema analysis and index recommendations.

MongoDB Server Configuration (mongod.conf)

Key parameters in mongod.conf affect performance and stability. On AWS EC2 instances, ensure you’re using appropriate instance types (e.g., memory-optimized or storage-optimized) and EBS volumes (e.g., `gp3` or `io1` for higher IOPS).

Key `mongod.conf` Parameters

# mongod.conf

storage:
  dbPath: /var/lib/mongodb
  journal:
    enabled: true
  # For SSDs, consider WiredTiger with compression
  engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 0.75 # Example: 75% of available RAM, adjust based on system usage

# network interfaces
net:
  port: 27017
  bindIp: 0.0.0.0 # Or specific IPs for security

# logging
systemLog:
  destination: file
  path: /var/log/mongodb/mongod.log
  logAppend: true
  verbosity: 0 # Increase for debugging, decrease for production

# operation profiling
# profiling:
#   slowOpThresholdMs: 100 # Log operations slower than 100ms
#   mode: slowOp # Log only slow operations

# Sharding (if applicable)
# sharding:
#   clusterRole: configsvr
#   configsvrFile: /var/lib/mongodb/mongod.conf
#   configdb: your_mongos_host:27019

# Security (essential for production)
# security:
#   authorization: enabled

The cacheSizeGB parameter is crucial. It dictates the WiredTiger cache size. A common recommendation is to allocate 50-75% of the system’s RAM to the WiredTiger cache, leaving enough for the OS and other processes. Monitor memory usage closely.

Connection Pooling

Ensure your Laravel application is configured to use connection pooling for MongoDB. Libraries like jenssegers/mongodb typically handle this automatically, but verify the pool size settings. An insufficient pool size can lead to connection exhaustion under load.

Example (config/database.php for jenssegers/mongodb)

<?php

return [

    // ... other configurations

    'mongodb' => [
        'driver' => 'mongodb',
        'host' => env('DB_HOST', 'localhost'),
        'port' => env('DB_PORT', 27017),
        'database' => env('DB_DATABASE'),
        'username' => env('DB_USERNAME'),
        'password' => env('DB_PASSWORD'),
        'options' => [
            'database' => env('DB_DATABASE'), // This is the database name
            'ssl' => env('DB_SSL', false),
            'replicaSet' => env('DB_REPLICA_SET'),
            'poolsize' => env('DB_POOL_SIZE', 5), // Default pool size, adjust as needed
        ],
    ],

    // ... other configurations
];

Adjust poolsize based on your application’s concurrency and MongoDB’s connection limits. Monitor the number of active connections in MongoDB.

AWS Specific Considerations

On AWS, leverage managed services where possible (e.g., Amazon RDS for relational databases, Amazon ElastiCache for caching). For self-managed MongoDB, consider:

  • EC2 Instance Types: Choose instance types that match your workload (e.g., m5 for general purpose, r5 for memory-intensive, i3 for I/O intensive).
  • EBS Volumes: Use gp3 for a good balance of performance and cost, or io1/io2 for demanding workloads requiring provisioned IOPS.
  • Network Configuration: Ensure security groups allow necessary traffic between your application servers and MongoDB instances. Use VPC endpoints for private connectivity.
  • Monitoring: Utilize Amazon CloudWatch for metrics on CPU, memory, disk I/O, and network traffic. Set up alarms for critical thresholds.

For MongoDB, consider Amazon DocumentDB if you want a managed, highly available, and scalable MongoDB-compatible database service. It offloads operational burdens like patching, backups, and scaling.

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