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

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

Nginx Configuration for High-Traffic WordPress

Optimizing Nginx for WordPress involves several key areas: caching, connection management, and static file serving. For a high-traffic WordPress site, especially one powered by PHP-FPM or Gunicorn, these settings are critical for reducing latency and server load.

Leveraging Nginx Caching

Nginx’s FastCGI cache is a powerful tool for serving dynamic WordPress content without hitting PHP-FPM or Gunicorn on every request. This drastically reduces database load and PHP execution time.

Nginx Cache Configuration Snippet

# Define cache path and parameters
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=wp_cache:100m inactive=60m max_size=10g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_valid 200 302 10m; # Cache successful responses for 10 minutes
fastcgi_cache_valid 404 1m;      # Cache 404s for 1 minute
fastcgi_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
fastcgi_cache_revalidate on;
fastcgi_cache_min_uses 1;
fastcgi_cache_lock on;

# Add cache-control headers for clients
add_header X-Cache-Status $upstream_cache_status;

# Exclude specific URIs from caching
# This is crucial for logged-in users, admin area, and dynamic actions
location ~* /(?:wp-admin/|wp-login\.php|xmlrpc\.php|feed/|embed/|comments/|trackback/) {
    fastcgi_cache_bypass 1;
    fastcgi_no_cache 1;
}

# Apply cache to WordPress front-end
location ~ \.php$ {
    # ... (your existing PHP-FPM/Gunicorn configuration) ...
    fastcgi_cache wp_cache; # Use the defined cache zone
    fastcgi_cache_methods GET HEAD; # Cache only GET and HEAD requests
    fastcgi_cache_valid 200 120m; # Extend cache for successful responses
    fastcgi_cache_valid 301 302 30m;
    fastcgi_cache_valid 404 5m;
    fastcgi_cache_lock_timeout 5s; # Prevent cache stampede
}

Explanation:

  • fastcgi_cache_path: Defines the directory for cache files, cache zone name (`wp_cache`), size of the shared memory zone (`100m`), and cache expiration (`inactive=60m`).
  • fastcgi_cache_key: A unique key for each cached item.
  • fastcgi_cache_valid: Sets the duration for which different HTTP status codes are cached.
  • fastcgi_cache_use_stale: Allows Nginx to serve stale cached content if the backend is unavailable or returns errors.
  • fastcgi_cache_lock: Prevents multiple requests for the same uncached resource from hitting the backend simultaneously (cache stampede).
  • add_header X-Cache-Status: Adds a header to responses indicating whether the request was a HIT, MISS, EXPIRED, etc., invaluable for debugging.
  • location ~* /(?:wp-admin/|wp-login\.php|...): This block explicitly bypasses the cache for administrative areas, login pages, feeds, and other dynamic endpoints. Adjust this regex as needed.

Connection and Worker Process Tuning

Optimizing worker processes and connection limits is crucial for handling concurrent requests efficiently.

Nginx Worker Configuration Snippet

worker_processes auto; # Or set to the number of CPU cores
worker_connections 4096; # Adjust based on system limits and expected load
multi_accept on;

events {
    worker_connections 4096; # Must be equal to or greater than worker_connections
    use epoll; # For Linux systems
}

# Increase file descriptor limits
worker_rlimit_nofile 65535;

Explanation:

  • worker_processes auto;: Nginx will automatically determine the number of worker processes based on the number of CPU cores.
  • worker_connections: The maximum number of simultaneous connections that each worker process can handle. This should be set considering the `ulimit -n` (open files limit) on your system.
  • multi_accept on;: Allows a worker to accept multiple new connections at once.
  • use epoll;: The preferred I/O multiplexing method on Linux for high performance.
  • worker_rlimit_nofile: Sets the maximum number of file descriptors that a worker process can open. This should be high enough to accommodate all connections and open files.

Optimizing Static File Serving

Nginx excels at serving static assets. Configure it to leverage browser caching and compression.

Static File Configuration Snippet

location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|webp|woff|woff2|ttf|eot)$ {
    expires 365d; # Set long expiry for static assets
    add_header Cache-Control "public, immutable";
    access_log off; # Optionally disable access logs for static files
    tcp_nopush on;
    tcp_nodelay on;
    open_file_cache max=2000 inactive=20s; # Cache file descriptors
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
}

# Enable 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;

Explanation:

  • expires 365d; and add_header Cache-Control "public, immutable";: Instructs browsers to cache these assets for a long period.
  • open_file_cache directives: Cache file descriptors and metadata for frequently accessed static files, reducing disk I/O.
  • gzip on; and related directives: Enable and configure Gzip compression for text-based assets, reducing bandwidth usage and improving load times.

Gunicorn/PHP-FPM Tuning for WordPress

The application server (Gunicorn for Python/WSGI apps, PHP-FPM for PHP) is the next critical layer. Proper configuration here directly impacts how quickly WordPress requests are processed after Nginx passes them on.

Gunicorn Configuration (WSGI)

If your WordPress site is running on a Python framework (e.g., Django with Wagtail, or a custom headless setup), Gunicorn is a common choice. Tuning involves worker processes, threads, and timeouts.

Gunicorn Command Line/Configuration Example

# Example command line invocation
gunicorn --workers 3 \
         --threads 2 \
         --worker-connections 100 \
         --timeout 120 \
         --bind 0.0.0.0:8000 \
         your_wsgi_app:application

# Or using a Gunicorn configuration file (gunicorn_config.py)
# workers = 3
# threads = 2
# worker_connections = 100
# timeout = 120
# bind = "0.0.0.0:8000"

Explanation:

  • --workers: The number of worker processes. A common starting point is (2 * CPU cores) + 1. For I/O-bound applications, more workers might be beneficial.
  • --threads: The number of threads per worker. This is useful for I/O-bound tasks, allowing a worker to handle multiple requests concurrently without blocking.
  • --worker-connections: Maximum number of simultaneous connections a worker can handle. This is often limited by the OS’s file descriptor limits.
  • --timeout: The maximum time (in seconds) a worker can spend processing a request before being killed. For WordPress, especially with complex plugins or API calls, this might need to be higher than for simpler applications.
  • --bind: The address and port Gunicorn listens on. Nginx will proxy requests to this.

PHP-FPM Configuration (PHP)

For traditional PHP-based WordPress, PHP-FPM is the standard. Tuning involves process management (static, dynamic, ondemand) and buffer sizes.

PHP-FPM Pool Configuration Snippet (e.g., `www.conf`)

; Example for PHP 7.4+
; Ensure this file is located in /etc/php/7.4/fpm/pool.d/www.conf (or similar)

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

; Process Manager Settings
; pm = dynamic ; or static or ondemand
; pm.max_children = 50
; pm.start_servers = 5
; pm.min_spare_servers = 2
; pm.max_spare_servers = 10
; pm.max_requests = 500

; For high-traffic, 'static' or 'ondemand' with careful tuning is often preferred.
; Let's use 'dynamic' as a common starting point and explain tuning.
pm = dynamic
pm.max_children = 100 ; Adjust based on available RAM and expected load
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 1000 ; Restart workers after X requests to prevent memory leaks

; Performance Tuning
request_terminate_timeout = 120 ; Similar to Gunicorn timeout
; php_admin_value[memory_limit] = 256M ; Adjust as needed per site/plugin requirements
; php_admin_value[max_execution_time] = 120 ; Adjust as needed
; php_admin_value[post_max_size] = 64M ; For uploads
; php_admin_value[upload_max_filesize] = 64M ; For uploads

; For larger sites, consider increasing opcache settings
; opcache.enable=1
; opcache.memory_consumption=128
; opcache.interned_strings_buffer=16
; opcache.max_accelerated_files=10000
; opcache.revalidate_freq=60
; opcache.validate_timestamps=0 ; Set to 1 in development, 0 in production for performance
; opcache.save_comments=1
; opcache.enable_file_override=0

Explanation:

  • pm: Process Manager.
    • dynamic: PHP-FPM spawns children as needed, up to max_children. Good balance.
    • static: PHP-FPM keeps a fixed number of children running. Best for predictable high load, but can waste resources if idle.
    • ondemand: Children are spawned only when requests arrive and killed after inactivity. Can have higher initial latency.
  • pm.max_children: The maximum number of child processes that will be created. This is the most critical setting and directly tied to your server’s RAM. Too high, and you’ll OOM kill. Too low, and requests will queue.
  • pm.start_servers, pm.min_spare_servers, pm.max_spare_servers: Control the dynamic scaling of child processes.
  • pm.max_requests: The number of requests each child process should execute before respawning. Helps mitigate memory leaks in plugins or PHP itself.
  • request_terminate_timeout: Similar to Gunicorn’s timeout.
  • php_admin_value: Directives to set PHP configuration values. Crucial for memory limits and execution times.
  • OPcache settings: Essential for PHP performance. Ensure it’s enabled and configured appropriately. Setting opcache.validate_timestamps=0 in production significantly boosts performance by avoiding file stat checks on every request.

DynamoDB Tuning for WordPress (Headless/API)

While not directly serving WordPress content in a traditional sense, DynamoDB is often used for headless WordPress setups, custom data storage, or as a caching layer. Optimizing it involves understanding provisioned throughput, auto-scaling, and data modeling.

Provisioned Throughput and Auto Scaling

DynamoDB is a fully managed NoSQL database. Its performance is governed by Read Capacity Units (RCUs) and Write Capacity Units (WCUs). For WordPress, consider how your data access patterns translate to these units.

Understanding RCUs and WCUs for WordPress Data

A Read Capacity Unit (RCU) represents one strongly consistent read per second, or two eventually consistent reads per second, for an item up to 4 KB in size. A Write Capacity Unit (WCU) represents one write per second for an item up to 1 KB in size.

For WordPress, common data types and their RCU/WCU implications:

  • Post/Page Data: If stored as individual items, a read for a single post (strongly consistent) might consume 1 RCU. If you fetch multiple posts in a single query (e.g., a list of recent posts), the RCU cost depends on the query and the total item size.
  • User Data: Similar to post data.
  • Comments: Fetching comments for a post can be a significant read operation.
  • Custom Fields/Meta: If stored as separate items or within a JSON attribute, reads/writes will vary.
  • Media Library Metadata: Typically smaller items, but potentially high read volume.

DynamoDB Auto Scaling Configuration

Auto Scaling allows DynamoDB to automatically adjust provisioned throughput based on actual traffic, preventing throttling and optimizing costs. For WordPress, this is crucial as traffic can fluctuate.

AWS CLI Example for Auto Scaling Setup

# Example: Setting up Auto Scaling for a DynamoDB table named 'wordpress_content'

# 1. Define the Auto Scaling Target
aws application-autoscaling put-scaling-policy \
    --service-namespace dynamodb \
    --resource-id table/wordpress_content \
    --scalable-dimension dynamodb:table:WriteCapacityUnits \
    --policy-name TargetTrackingWriteCapacity \
    --target-tracking-scaling-policy-configuration '{
        "TargetValue": 0.7,
        "PredefinedMetricSpecification": {
            "PredefinedMetric": "DynamoDBWriteCapacityUtilization"
        },
        "ScaleInCooldown": 60,
        "ScaleOutCooldown": 60
    }'

aws application-autoscaling put-scaling-policy \
    --service-namespace dynamodb \
    --resource-id table/wordpress_content \
    --scalable-dimension dynamodb:table:ReadCapacityUnits \
    --policy-name TargetTrackingReadCapacity \
    --target-tracking-scaling-policy-configuration '{
        "TargetValue": 0.7,
        "PredefinedMetric": {
            "PredefinedMetric": "DynamoDBReadCapacityUtilization"
        },
        "ScaleInCooldown": 60,
        "ScaleOutCooldown": 60
    }'

# 2. Set the minimum and maximum provisioned capacity
aws dynamodb update-table \
    --table-name wordpress_content \
    --provisioned-throughput \
    ReadCapacityUnits=5,WriteCapacityUnits=5 \
    --billing-mode PROVISIONED

# Note: The above command sets the initial provisioned throughput.
# Auto Scaling will manage changes between min/max.
# To set min/max for Auto Scaling, you typically do this via the AWS Console
# or by creating a separate Auto Scaling Group configuration.
# For CLI, you'd use `aws application-autoscaling register-scalable-target` first.

# Example of registering a scalable target (often done implicitly by PutScalingPolicy, but good to know)
aws application-autoscaling register-scalable-target \
    --service-namespace dynamodb \
    --resource-id table/wordpress_content \
    --scalable-dimension dynamodb:table:WriteCapacityUnits \
    --min-capacity 5 \
    --max-capacity 500

aws application-autoscaling register-scalable-target \
    --service-namespace dynamodb \
    --resource-id table/wordpress_content \
    --scalable-dimension dynamodb:table:ReadCapacityUnits \
    --min-capacity 5 \
    --max-capacity 500

Explanation:

  • aws application-autoscaling put-scaling-policy: Configures a scaling policy.
  • --service-namespace dynamodb: Specifies the AWS service.
  • --resource-id table/wordpress_content: Identifies the specific DynamoDB table.
  • --scalable-dimension: The metric to scale on (Read or Write Capacity Units).
  • --policy-name: A descriptive name for the policy.
  • --target-tracking-scaling-policy-configuration:
    • TargetValue: The desired utilization percentage (e.g., 70%). DynamoDB will try to maintain this level.
    • PredefinedMetric: Uses AWS-managed metrics like DynamoDBReadCapacityUtilization.
    • ScaleInCooldown/ScaleOutCooldown: Time (in seconds) to wait before allowing a scale-in or scale-out after a previous scaling activity.
  • aws dynamodb update-table --billing-mode PROVISIONED: Ensures the table is in Provisioned mode, which is required for Auto Scaling. You set initial ReadCapacityUnits and WriteCapacityUnits here.
  • aws application-autoscaling register-scalable-target: Defines the minimum and maximum capacity for the resource. This is crucial for cost control and preventing excessive scaling.

Data Modeling for Performance

For WordPress data in DynamoDB, efficient data modeling is paramount. Avoid single-table design anti-patterns if they lead to complex scans. Consider using Global Secondary Indexes (GSIs) and Local Secondary Indexes (LSIs) effectively.

Example: Storing Posts and Comments

A common pattern is to store related items in the same table using a composite primary key (Partition Key + Sort Key).

{
    "PK": "POST#12345",
    "SK": "METADATA",
    "post_title": "My Awesome Post",
    "post_content": "...",
    "post_author": "user_id_abc",
    "created_at": "2023-10-27T10:00:00Z",
    "updated_at": "2023-10-27T10:00:00Z",
    "entity_type": "Post"
}
{
    "PK": "POST#12345",
    "SK": "COMMENT#67890",
    "comment_author": "commenter_xyz",
    "comment_content": "Great post!",
    "created_at": "2023-10-27T10:05:00Z",
    "entity_type": "Comment"
}
{
    "PK": "POST#12345",
    "SK": "COMMENT#67891",
    "comment_author": "commenter_uvw",
    "comment_content": "Very informative.",
    "created_at": "2023-10-27T10:10:00Z",
    "entity_type": "Comment"
}

Explanation:

  • Partition Key (PK): POST#12345. This groups all items related to a specific post.
  • Sort Key (SK): Differentiates items within the same partition. METADATA for the post itself, and COMMENT#ID for individual comments.
  • Querying Comments: A query using PK = "POST#12345" and SK begins_with "COMMENT#" efficiently retrieves all comments for a post. This is a single read operation (costing RCUs based on total item size).
  • Entity Type: Useful for filtering if you store multiple entity types in the same table.

This approach minimizes the need for scans and leverages DynamoDB’s strengths for efficient retrieval of related data, crucial for headless WordPress APIs.

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