• 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 PostgreSQL on AWS for WordPress

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and PostgreSQL on AWS for WordPress

Nginx Configuration for High-Traffic WordPress

Optimizing Nginx for WordPress involves fine-tuning several key directives to handle concurrent connections, cache static assets effectively, and manage request processing. For a typical WordPress setup on AWS, we’ll focus on directives that impact performance under load.

Worker Processes and Connections

The `worker_processes` directive determines how many worker processes Nginx will spawn. Setting this to `auto` allows Nginx to detect the number of CPU cores and utilize them efficiently. `worker_connections` defines 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.

Caching Strategies

Leveraging Nginx’s built-in caching for static assets (CSS, JS, images) significantly reduces the load on your application servers and database. We’ll configure `expires` headers to instruct browsers and intermediate caches on how long to store these assets. For dynamic content, consider using Nginx’s FastCGI cache or proxy cache if you’re using a separate caching layer like Varnish or Redis Object Cache Pro.

Gzip Compression and Keepalive

Enabling Gzip compression reduces the size of text-based assets sent over the network, improving load times. `keepalive_timeout` and `keepalive_requests` help maintain persistent connections, reducing the overhead of establishing new TCP connections for subsequent requests.

Example Nginx Configuration Snippet

Here’s a sample configuration block for your nginx.conf or a site-specific configuration file (e.g., /etc/nginx/sites-available/wordpress):

# /etc/nginx/nginx.conf or /etc/nginx/sites-available/wordpress

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;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 1000; # Increased from 100
    types_hash_max_size 2048;

    server_tokens off; # Hide Nginx version for security

    # Gzip Compression
    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 image/svg+xml;

    # Caching for static assets
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|webp|woff|woff2|ttf|eot|otf)$ {
        expires 30d; # Cache for 30 days
        add_header Cache-Control "public, no-transform";
        access_log off; # Optional: reduce log noise for static files
        log_not_found off;
    }

    # Proxy to PHP-FPM or Gunicorn
    location / {
        try_files $uri $uri/ /index.php?$args; # For PHP-FPM
        # For Gunicorn (adjust proxy_pass to your Gunicorn endpoint)
        # proxy_pass http://unix:/run/gunicorn.sock;
        # 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;
    }

    # PHP-FPM configuration (if applicable)
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; # Adjust PHP version and socket path
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # Include other configurations
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

After modifying the Nginx configuration, always test it before reloading:

sudo nginx -t
sudo systemctl reload nginx

Gunicorn/PHP-FPM Tuning for WordPress Performance

The application server (Gunicorn for Python-based frameworks, or PHP-FPM for PHP) is crucial for processing dynamic requests. Tuning these components directly impacts response times and the ability to handle concurrent users.

Gunicorn Configuration (Python/WSGI)

For Python applications serving WordPress (e.g., using Django or Flask with a WordPress API), Gunicorn’s worker count and type are paramount. The number of workers should generally be 2-4 times the number of CPU cores, depending on whether your application is CPU-bound or I/O-bound. Using the gevent worker class can significantly improve concurrency for I/O-bound tasks.

Example Gunicorn Command Line/Configuration

# Example command line for Gunicorn
gunicorn --workers 4 \
         --worker-class gevent \
         --bind unix:/run/gunicorn.sock \
         --timeout 120 \
         --log-level info \
         your_wsgi_app:application

# Or using a Gunicorn configuration file (e.g., gunicorn_config.py)
# workers = 4
# worker_class = 'gevent'
# bind = 'unix:/run/gunicorn.sock'
# timeout = 120
# loglevel = 'info'

The --timeout parameter is important; set it high enough to accommodate long-running operations but not so high that it masks performance issues.

PHP-FPM Configuration (PHP)

For traditional PHP WordPress installations, PHP-FPM’s process management is key. The pm (process manager) setting can be set to dynamic, static, or ondemand. dynamic is often a good balance, allowing FPM to scale the number of child processes based on demand. static is useful for predictable, high-load environments where you want to pre-allocate processes.

Tuning `pm.max_children`, `pm.start_servers`, `pm.min_spare_servers`, `pm.max_spare_servers`

These directives control the number of PHP worker processes. A common starting point for pm.max_children is to calculate based on available RAM. Each PHP-FPM worker can consume a certain amount of memory (e.g., 20-50MB). If your server has 8GB of RAM and you reserve 2GB for the OS and other services, you have 6GB (6144MB) for PHP. If each worker uses 30MB, you can have roughly 200 children. However, it’s crucial to monitor actual memory usage.

Example PHP-FPM Configuration Snippet

; /etc/php/7.4/fpm/pool.d/www.conf (adjust version and pool name as needed)

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

; Process Manager Settings
pm = dynamic
pm.max_children = 150       ; Adjust based on RAM and monitoring
pm.start_servers = 10       ; Number of servers started when FPM starts
pm.min_spare_servers = 5    ; Minimum number of idle servers
pm.max_spare_servers = 25   ; Maximum number of idle servers
pm.max_requests = 500       ; Restart a child after this many requests

; Other useful settings
request_terminate_timeout = 120s ; Timeout for script execution
; rlimit_files = 1024
; rlimit_core = 0

After changes, restart PHP-FPM:

sudo systemctl restart php7.4-fpm

PostgreSQL Tuning for WordPress Database Performance

The PostgreSQL database is often the bottleneck for high-traffic WordPress sites. Tuning involves adjusting memory allocation, connection pooling, and query optimization.

Key PostgreSQL Configuration Parameters

The primary configuration file is postgresql.conf. Key parameters to tune include:

  • shared_buffers: This is the most critical parameter. It defines the amount of memory dedicated to PostgreSQL for caching data and indexes. A common recommendation is 25% of system RAM, but this can be higher (up to 40%) on dedicated database servers with ample RAM.
  • work_mem: Memory used for internal sort operations and hash tables. If your queries involve complex sorts or joins, increasing this can help. Be cautious, as this memory is allocated per sort operation within a query, so a high value with many concurrent queries can exhaust RAM.
  • maintenance_work_mem: Memory used for maintenance operations like VACUUM, CREATE INDEX, and ALTER TABLE. Increasing this can speed up these operations.
  • effective_cache_size: An estimate of 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 helps the query planner make better decisions.
  • max_connections: The maximum number of concurrent connections allowed. WordPress sites often have many short-lived connections. Ensure this is high enough but not so high that it over-allocates memory per connection.
  • wal_buffers: Memory for WAL (Write-Ahead Logging) data. A value of -1 (auto) is often sufficient, but increasing it can improve write performance.
  • checkpoint_completion_target: Controls how spread out checkpoints are. A value of 0.9 helps reduce I/O spikes during checkpoints.

Example `postgresql.conf` Snippet

# /etc/postgresql/12/main/postgresql.conf (adjust version and path as needed)

# Memory settings
shared_buffers = 2GB             # Example for a server with 8GB RAM
work_mem = 32MB                  # Adjust based on query complexity
maintenance_work_mem = 256MB     # For VACUUM, CREATE INDEX etc.
effective_cache_size = 4GB       # Estimate of OS + shared_buffers cache

# Connection settings
max_connections = 200            # Adjust based on expected load and available RAM

# WAL settings
wal_buffers = 16MB               # Can improve write performance
checkpoint_completion_target = 0.9

# Logging (useful for debugging)
log_statement = 'ddl'            # Log Data Definition Language statements
# log_min_duration_statement = 250ms # Log queries longer than 250ms

# Other settings
random_page_cost = 1.1           # Lower for SSDs
seq_page_cost = 1.0
default_statistics_target = 100  # For better query planning


After modifying postgresql.conf, you need to restart the PostgreSQL service:

sudo systemctl restart postgresql

Connection Pooling with PgBouncer

WordPress's frequent, short-lived database connections can be a performance drain. PgBouncer is a lightweight connection pooler that significantly reduces the overhead of establishing new connections to PostgreSQL. It maintains a pool of active connections and hands them out to applications as needed.

Example PgBouncer Configuration

# /etc/pgbouncer/pgbouncer.ini

[databases]
# Format: dbname = PostgreSQL connection string
# Example: wordpress_db = host=127.0.0.1 port=5432 dbname=wordpress_db user=wp_user password=wp_password

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = session             ; 'session' is generally best for WordPress
max_client_conn = 1000          ; Max connections from clients (e.g., WordPress app)
default_pool_size = 20          ; Pool size per database
min_pool_size = 5               ; Min pool size per database
reserve_pool_size = 5           ; Pool size for reserved connections
reserve_pool_timeout = 5
; logfile = /var/log/postgresql/pgbouncer.log
; pidfile = /var/run/pgbouncer/pgbouncer.pid

You'll also need to configure userlist.txt with the database credentials:

"wp_user" "wp_password"

Start and enable PgBouncer:

sudo systemctl start pgbouncer
sudo systemctl enable pgbouncer

Ensure your WordPress wp-config.php (or application configuration) points to PgBouncer's port (e.g., 6432) instead of the direct PostgreSQL port (5432).

AWS Specific Considerations

When deploying on AWS, several factors come into play:

  • Instance Types: Choose EC2 instance types that balance CPU, RAM, and Network I/O. For databases, consider RDS instances with Provisioned IOPS (io1/io2) for predictable storage performance.
  • EBS Volumes: For EC2 instances, use appropriate EBS volume types (gp3 for general purpose, io1/io2 for high-performance databases) and provision sufficient IOPS and throughput.
  • VPC Networking: Ensure your security groups and network ACLs are configured correctly to allow traffic between Nginx, your application server, and the database, while restricting unnecessary access.
  • Load Balancers (ALB/NLB): If using AWS Elastic Load Balancing, configure health checks appropriately and consider sticky sessions if your application requires them (though stateless is preferred).
  • RDS Performance Insights: Utilize RDS Performance Insights to identify database bottlenecks, slow queries, and wait events.

Monitoring and Iteration

Performance tuning is an iterative process. Implement these changes incrementally and monitor your system's performance using tools like:

  • Nginx Amplify: For Nginx metrics.
  • Prometheus/Grafana: For comprehensive system and application metrics.
  • AWS CloudWatch: For EC2, RDS, and ELB metrics.
  • New Relic/Datadog: APM tools for deeper application insights.
  • PostgreSQL `pg_stat_statements` extension: To identify slow queries.

Regularly review logs, performance metrics, and database query performance to identify areas for further optimization.

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