• 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 Google Cloud for PHP

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

Nginx as a High-Performance Frontend Proxy

For PHP applications, Nginx excels as a static file server and a reverse proxy to your application server (Gunicorn for Python/WSGI, or PHP-FPM for PHP). Optimizing Nginx involves tuning worker processes, connection handling, and caching mechanisms. On Google Cloud, leveraging Compute Engine instances with appropriate machine types and network configurations is foundational.

Nginx Worker Processes and Connections

The number of worker processes should ideally match the number of CPU cores available to the Nginx instance. This allows for true parallel processing. The worker_connections directive dictates the maximum number of simultaneous connections a single worker process can handle. A common starting point is 1024, but this can be increased based on application load and available memory.

Tuning nginx.conf

Locate your main Nginx configuration file, typically /etc/nginx/nginx.conf. Modify the events block as follows:

user www-data;
worker_processes auto; # 'auto' will detect and use the number of CPU cores
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

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

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

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

    # SSL configuration
    # ...

    # 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/*;
}

After modifying nginx.conf, test the configuration and reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

Optimizing PHP-FPM for PHP Applications

PHP-FPM (FastCGI Process Manager) is the de facto standard for serving PHP applications. Its performance is heavily influenced by the process manager settings, specifically the number of child processes and how they are managed.

PHP-FPM Process Manager Settings

The primary configuration file for PHP-FPM is typically located at /etc/php/[version]/fpm/php-fpm.conf or within a pool configuration file like /etc/php/[version]/fpm/pool.d/www.conf. The pm (process manager) setting is crucial. Common options are static, dynamic, and ondemand.

  • static: Keeps a fixed number of child processes running. Good for predictable high loads.
  • dynamic: Starts with a minimum number of processes and spawns more up to a maximum as needed.
  • ondemand: Starts no processes initially and spawns them only when a request arrives.

For most production environments with consistent traffic, dynamic or static offers better performance than ondemand due to reduced latency in process startup. The following settings in www.conf are recommended:

; /etc/php/8.1/fpm/pool.d/www.conf (example for PHP 8.1)
; Adjust these values based on your server's CPU and memory.
; For a 4-core CPU, you might start with pm.max_children = 100.

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

; Process Manager settings
pm = dynamic
pm.max_children = 100       ; Max number of child processes at any time
pm.start_servers = 5        ; Number of child processes started at FPM startup
pm.min_spare_servers = 2    ; Minimum number of idle processes
pm.max_spare_servers = 10   ; Maximum number of idle processes
pm.process_idle_timeout = 10s ; How long an idle process is kept before being killed
pm.max_requests = 500       ; Max requests a child process will serve before respawning

; Other useful settings
request_terminate_timeout = 60s ; Timeout for script execution
; rlimit_files = 1024
; rlimit_nofile = 65536
; chdir = /

After changes, test and restart PHP-FPM:

sudo php-fpm8.1 -t # Replace 8.1 with your PHP version
sudo systemctl restart php8.1-fpm # Replace 8.1 with your PHP version

Nginx Configuration for PHP-FPM

Your Nginx site configuration (e.g., /etc/nginx/sites-available/your-app) needs to correctly pass requests to PHP-FPM. Ensure the fastcgi_pass directive points to your PHP-FPM socket or IP address.

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

    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's listen directive
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        # Or for TCP/IP: fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

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

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

Remember to enable the site and reload Nginx:

sudo ln -s /etc/nginx/sites-available/your-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

PostgreSQL Performance Tuning on Google Cloud

PostgreSQL performance is critical for data-intensive PHP applications. Tuning involves adjusting PostgreSQL configuration parameters (postgresql.conf) and optimizing database schema and queries.

Key PostgreSQL Configuration Parameters

The primary configuration file is postgresql.conf. On Google Cloud, ensure your PostgreSQL instance (Cloud SQL or self-managed on Compute Engine) has sufficient CPU and RAM. The following parameters are essential for performance tuning:

# postgresql.conf - Example tuning parameters
# Adjust based on your instance size (vCPU, RAM) and workload.
# For a 16GB RAM instance, these are reasonable starting points.

# Memory
shared_buffers = 4GB             # Typically 25% of system RAM. Crucial for caching.
work_mem = 64MB                  # For sorting and hashing. Increase if complex queries are slow.
maintenance_work_mem = 512MB     # For VACUUM, CREATE INDEX, etc.
effective_cache_size = 12GB      # Estimate of total cache available for PostgreSQL (OS + shared_buffers).

# Checkpointing
max_wal_size = 4GB               # Controls frequency of WAL checkpoints. Higher values reduce I/O but increase recovery time.
min_wal_size = 1GB               # Minimum WAL size to keep.
checkpoint_completion_target = 0.9 # Spreads checkpoints over time.

# Connections
max_connections = 200            # Number of concurrent connections. Adjust based on application needs and server resources.
# listen_addresses = '*'         # Or specific IPs if needed.

# Autovacuum
autovacuum = on
autovacuum_max_workers = 3
autovacuum_naptime = 15s
autovacuum_vacuum_threshold = 50
autovacuum_analyze_threshold = 50

# Logging
log_destination = 'stderr'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_statement = 'ddl'            # Log Data Definition Language statements. Consider 'all' for debugging.
log_min_duration_statement = 250ms # Log statements that take longer than this. Crucial for query optimization.

# Other important settings
random_page_cost = 1.1           # For SSDs, lower this from default 4.0.
seq_page_cost = 1.0              # Default is fine.
default_statistics_target = 100  # For more detailed query plans.
max_worker_processes = 8         # Number of CPU cores.
max_parallel_workers = 4         # Number of parallel workers for parallel query execution.
max_parallel_workers_per_gather = 2 # Max workers per Gather node.

After modifying postgresql.conf, you need to reload or restart the PostgreSQL service. For most parameters, a reload is sufficient, but some (like shared_buffers, max_connections) require a restart.

# For Cloud SQL, use the Google Cloud Console or gcloud CLI to update flags.
# For self-managed PostgreSQL:
sudo systemctl reload postgresql # Or restart if needed

Query Optimization and Indexing

Even with perfect configuration, slow queries will cripple performance. Use EXPLAIN ANALYZE to understand query execution plans and identify bottlenecks. Regularly review logs for slow statements.

-- Example of analyzing a query
EXPLAIN ANALYZE
SELECT u.name, o.order_date
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.registration_date > '2023-01-01'
ORDER BY o.order_date DESC
LIMIT 10;

-- Identify missing indexes
-- Look for sequential scans on large tables in the EXPLAIN ANALYZE output.
-- Example: CREATE INDEX idx_orders_user_id ON orders (user_id);
-- Example: CREATE INDEX idx_users_registration_date ON users (registration_date);

Consider using tools like pg_stat_statements extension for a more systematic way to track and analyze query performance over time.

-- Enable pg_stat_statements (requires restart)
-- In postgresql.conf:
-- shared_preload_libraries = 'pg_stat_statements'
-- After restart:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- Query for slow statements
SELECT
    query,
    calls,
    total_time,
    mean_time,
    rows
FROM
    pg_stat_statements
ORDER BY
    total_time DESC
LIMIT 10;

Google Cloud Specific Considerations

When deploying on Google Cloud, several factors influence performance:

  • Machine Types: Choose Compute Engine machine types that balance CPU, RAM, and network bandwidth for your Nginx and application servers. For databases, Cloud SQL offers managed instances with predictable performance.
  • Network Latency: Ensure your Nginx, application servers, and PostgreSQL instances are in the same Google Cloud region and, if possible, the same zone to minimize latency.
  • Persistent Disks: For self-managed PostgreSQL on Compute Engine, use SSD Persistent Disks for better I/O performance.
  • Cloud SQL Proxy: If using Cloud SQL, the Cloud SQL Auth Proxy provides secure and reliable connectivity. Ensure your application server is configured to use it.
  • Load Balancing: Google Cloud Load Balancing can distribute traffic across multiple Nginx instances, improving availability and scalability. Configure health checks to ensure traffic is only sent to healthy instances.

Regular monitoring of CPU utilization, memory usage, network traffic, and database connection counts using Google Cloud’s operations suite (formerly Stackdriver) is essential for identifying performance bottlenecks and validating tuning efforts.

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