• 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 PostgreSQL on DigitalOcean for PHP

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and PostgreSQL on DigitalOcean for PHP

Nginx Configuration for High-Traffic PHP Applications

Optimizing Nginx is paramount for serving PHP applications efficiently. We’ll focus on key directives that impact concurrency, caching, and request handling. This assumes a standard DigitalOcean droplet setup with Nginx installed.

Worker Processes and Connections

The `worker_processes` directive dictates how many worker processes Nginx will spawn. Setting this to `auto` is generally recommended, allowing Nginx to determine the optimal number based on available CPU cores. The `worker_connections` directive sets the maximum number of simultaneous connections that each worker process can handle. A common starting point is 1024, but this can be increased based on your server’s RAM and expected load.

Edit your main Nginx configuration file, typically located at /etc/nginx/nginx.conf:

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 4096; # Increased from 1024
    multi_accept on;
}

Keepalive Timeout and Buffers

keepalive_timeout controls how long an idle HTTP connection will remain open. A shorter timeout can free up resources faster, while a longer one can improve performance for clients making frequent requests. client_body_buffer_size and client_header_buffer_size are crucial for handling large requests. Ensure they are sufficiently large to accommodate typical request sizes.

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65; # Default is 65, adjust as needed
    types_hash_max_size 2048;

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

    # Buffers
    client_body_buffer_size 10M; # Increased for larger uploads
    client_header_buffer_size 1k;
    large_client_header_buffers 4 16k; # For large headers

    # ... other http configurations
}

Gzip Compression

Enabling Gzip compression significantly reduces the size of responses sent to the client, leading to faster page loads. Configure it within the http block.

http {
    # ... other http configurations

    gzip on;
    gzip_disable "msie6"; # Disable for older IE versions
    gzip_vary on;
    gzip_proxied any; # Compress proxied responses
    gzip_comp_level 6; # Compression level (1-9)
    gzip_buffers 16 8k; # Number and size of buffers
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}

Caching Static Assets

Leverage browser caching for static assets like CSS, JavaScript, and images. This reduces server load and speeds up repeat visits for users.

http {
    # ... other http configurations

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

PHP-FPM Configuration Tuning

For PHP applications, Nginx typically communicates with PHP-FPM (FastCGI Process Manager). Tuning PHP-FPM is critical for managing PHP processes and memory. The primary configuration file is usually /etc/php/[version]/fpm/php.ini and /etc/php/[version]/fpm/pool.d/www.conf.

Process Manager Settings (www.conf)

The pm (process manager) setting determines how PHP-FPM manages child processes. dynamic is a good balance for most applications, scaling based on demand. static can offer more predictable performance but might waste resources if idle. ondemand is resource-efficient but can introduce latency on initial requests.

; /etc/php/8.1/fpm/pool.d/www.conf (example for PHP 8.1)
[www]
user = www-data
group = www-data
listen = /run/php/php8.1-fpm.sock # Or a TCP socket like 127.0.0.1:9000

; Process Manager settings
pm = dynamic
pm.max_children = 50       ; Max number of child processes at any time
pm.start_servers = 5       ; Number of child processes started at boot
pm.min_spare_servers = 2   ; Min number of idle processes
pm.max_spare_servers = 10  ; Max number of idle processes
pm.process_idle_timeout = 10s; How long an idle process will live
pm.max_requests = 500      ; Max requests a child process will serve before respawning

Tuning `pm.max_children`: This is the most critical setting. It should be set based on your server’s RAM. A common formula is (Total RAM - RAM for OS/Nginx/DB) / Average PHP Process Size. Monitor memory usage and adjust accordingly. If you see OOM killer events, this is too high. If your server is consistently underutilized and requests are queued, it might be too low.

PHP Memory Limits and Execution Time (php.ini)

Adjusting memory_limit and max_execution_time in php.ini is essential for handling complex scripts and large data operations.

; /etc/php/8.1/fpm/php.ini (example for PHP 8.1)
memory_limit = 256M       ; Increase if your application requires more memory
max_execution_time = 60   ; Max execution time in seconds
upload_max_filesize = 64M ; Max size of an uploaded file
post_max_size = 64M       ; Max size of POST data that is accepted

Nginx to PHP-FPM Configuration

Ensure your Nginx site configuration correctly points to your PHP-FPM socket or TCP address. This is typically found in /etc/nginx/sites-available/[your_site].

server {
    # ... other server configurations

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        # With php-fpm (or other unix sockets):
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        # With php-cgi (or other tcp sockets):
        # fastcgi_pass 127.0.0.1:9000;
    }

    # Deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    location ~ /\.ht {
        deny all;
    }
}

PostgreSQL Performance Tuning

PostgreSQL’s performance is heavily influenced by its configuration parameters, primarily managed in postgresql.conf. The location varies by version and distribution but is often found in /etc/postgresql/[version]/main/postgresql.conf.

Shared Buffers and WAL Tuning

shared_buffers is arguably the most important parameter. It defines the amount of memory PostgreSQL can use for caching data pages. A common recommendation is 25% of system RAM, but this can be tuned higher (up to 40%) if the server is dedicated to PostgreSQL and has ample RAM. wal_buffers is for Write-Ahead Log buffering; a value of -1 lets PostgreSQL choose a sensible default (typically 1/32 of shared_buffers, up to 16MB).

# /etc/postgresql/14/main/postgresql.conf (example for PostgreSQL 14)
shared_buffers = 1GB       ; Example for a server with 4GB RAM
wal_buffers = 16MB         ; Default is usually fine, but can be increased
wal_level = replica        ; Or 'logical' if using logical replication
fsync = on                 ; Essential for data integrity
synchronous_commit = on    ; For ACID compliance, can be 'local' or 'off' for performance at risk
wal_writer_delay = 200ms   ; How often WAL writer flushes data to disk
commit_delay = 10ms        ; Delay before committing to group WAL records
commit_siblings = 5        ; Number of concurrent commits to trigger commit_delay

Effective Cache Size and Maintenance Work Mem

effective_cache_size tells the query planner how much memory is available for disk caching by the operating system and PostgreSQL’s shared buffers. Setting this to 50-75% of total RAM is a good starting point. maintenance_work_mem is used for maintenance operations like VACUUM, CREATE INDEX, and ALTER TABLE. Increasing this can significantly speed up these operations.

# /etc/postgresql/14/main/postgresql.conf
effective_cache_size = 2GB ; Example for a server with 4GB RAM
maintenance_work_mem = 256MB ; Increase for faster index creation and vacuuming
work_mem = 16MB              ; Memory for sorts, hashes, etc. per operation. Tune based on query analysis.
temp_buffers = 8MB           ; Memory for temporary tables

Autovacuum Tuning

Autovacuum is crucial for reclaiming space from dead tuples and preventing table bloat. Tuning its parameters can prevent performance degradation. Ensure it’s enabled and adjust thresholds based on your workload.

# /etc/postgresql/14/main/postgresql.conf
autovacuum = on
log_autovacuum_min_duration = 0 ; Log all autovacuum actions
autovacuum_max_workers = 3      ; Number of concurrent autovacuum processes
autovacuum_naptime = 1min       ; How often autovacuum checks for work
autovacuum_vacuum_threshold = 50 ; Min number of rows to trigger vacuum
autovacuum_analyze_threshold = 50 ; Min number of rows to trigger analyze
autovacuum_vacuum_scale_factor = 0.1 ; Fraction of table size to trigger vacuum
autovacuum_analyze_scale_factor = 0.05 ; Fraction of table size to trigger analyze

Connection Pooling

For applications with many short-lived connections, using a connection pooler like PgBouncer can significantly reduce overhead. Configure PgBouncer separately and point your application to its connection string.

Monitoring and Iterative Tuning

Tuning is an iterative process. Use monitoring tools to observe key metrics:

  • Nginx: Active connections, request rates, error rates (nginx-status module or Prometheus exporters).
  • PHP-FPM: Process count, request latency, memory usage (php-fpm-status or Prometheus exporters).
  • PostgreSQL: Cache hit ratio, active connections, query execution times, I/O wait, replication lag (pg_stat_activity, pg_stat_database, pg_stat_statements, Prometheus exporters).
  • System: CPU utilization, memory usage, disk I/O, network traffic.

Tools like htop, iotop, pg_top, and Prometheus with Grafana are invaluable. Always make one change at a time, monitor its impact, and document your findings. For PostgreSQL, the pgtune script can provide a good starting point for configuration recommendations based on your hardware.

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