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.