• 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 Elasticsearch on AWS for Shopify

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and Elasticsearch on AWS for Shopify

Nginx as a High-Performance Frontend for Shopify Applications

When deploying a Shopify application backend on AWS, Nginx serves as the critical entry point, handling SSL termination, static asset serving, request routing, and load balancing. Optimizing Nginx is paramount for low latency and high throughput. We’ll focus on key directives and configurations for a production environment.

Nginx Configuration Tuning

The core of Nginx performance lies in its worker processes and connection handling. For a typical EC2 instance, setting the number of worker processes to match the number of CPU cores is a good starting point. The worker_connections directive dictates the maximum number of simultaneous connections a worker can handle.

Core Nginx Directives

In your nginx.conf (typically located at /etc/nginx/nginx.conf or within /etc/nginx/conf.d/), adjust the following:

user www-data;
worker_processes auto; # Or set to the number of CPU cores, e.g., worker_processes 4;

events {
    worker_connections 4096; # Adjust based on expected load and system limits
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off; # Important for security

    # Gzip compression for text-based assets
    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;

    # Caching for static assets
    # Define cache zones for different asset types
    proxy_cache_path /var/cache/nginx/static levels=1:2 keys_zone=static_cache:10m max_size=1g inactive=60m;
    proxy_cache_key "$scheme$request_method$host$request_uri";

    # Include other configurations
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Access log configuration (consider disabling or using a separate logging service for high traffic)
    # access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;

    # Define your upstream servers (Gunicorn/FPM)
    upstream app_servers {
        # Use least_conn for better distribution if backend servers have varying capacities
        # least_conn;
        server 10.0.1.10:8000; # Example Gunicorn worker
        server 10.0.1.11:8000; # Example Gunicorn worker
        # Or for PHP-FPM
        # server unix:/var/run/php/php7.4-fpm.sock;
    }

    server {
        listen 80;
        server_name your-shopify-app.com;

        # Redirect HTTP to HTTPS
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl http2;
        server_name your-shopify-app.com;

        ssl_certificate /etc/letsencrypt/live/your-shopify-app.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/your-shopify-app.com/privkey.pem;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers on;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_session_tickets off;

        # Static file serving with caching
        location /static/ {
            alias /path/to/your/static/files/;
            expires 30d;
            add_header Cache-Control "public, immutable";
            proxy_cache static_cache;
            proxy_cache_valid 200 302 10m;
            proxy_cache_valid 404 1m;
            proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
            access_log off; # Disable access logs for static files if not needed
        }

        # Proxy requests to the application backend
        location / {
            proxy_pass http://app_servers;
            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;

            # WebSocket support (if your app uses them)
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";

            # Timeout settings
            proxy_connect_timeout 60s;
            proxy_send_timeout 60s;
            proxy_read_timeout 60s;
        }
    }
}

Key Nginx Optimizations Explained:

  • worker_processes auto;: Dynamically adjusts worker processes based on available CPU cores.
  • worker_connections 4096;: Increases the maximum number of concurrent connections per worker. Ensure your OS limits (ulimit -n) are set appropriately.
  • sendfile on;: Efficiently transfers files from disk to network socket without user-space buffering.
  • tcp_nopush on;: Instructs Nginx to send headers in one packet and the data in another, reducing network overhead.
  • tcp_nodelay on;: Disables the Nagle algorithm, which can improve latency for real-time applications.
  • keepalive_timeout 65;: Keeps connections open for a specified duration, reducing the overhead of establishing new connections.
  • gzip on;: Enables Gzip compression for supported MIME types, significantly reducing bandwidth usage and improving load times.
  • server_tokens off;: Hides the Nginx version number, a minor security hardening measure.
  • proxy_cache_path and proxy_cache: Implements Nginx-level caching for static assets, offloading requests from your application servers.
  • ssl_protocols, ssl_ciphers, ssl_session_cache: Configures strong TLS settings for secure and efficient HTTPS connections.
  • proxy_set_header directives: Crucial for passing accurate client information to the backend application.
  • proxy_http_version 1.1;, proxy_set_header Upgrade, proxy_set_header Connection "upgrade";: Essential for WebSocket support.

Tuning Gunicorn (Python/Django/Flask)

For Python-based Shopify backends (e.g., using Django or Flask), Gunicorn is a common WSGI HTTP Server. Its performance is dictated by the number of worker processes and threads.

Gunicorn Worker Configuration

The recommended worker type is typically gevent or event (which is the default and uses Python’s asyncio or gevent if available). The number of workers should generally be (2 * number_of_cores) + 1. For I/O-bound applications, increasing the number of worker threads within each worker process can be beneficial.

Example Gunicorn startup command:

gunicorn --workers 3 \
         --worker-class gevent \
         --bind 0.0.0.0:8000 \
         --threads 2 \
         --timeout 120 \
         your_project.wsgi:application

Gunicorn Tuning Parameters:

  • --workers 3: Sets the number of worker processes. Adjust based on your EC2 instance’s CPU cores.
  • --worker-class gevent: Uses gevent for asynchronous I/O, which is highly effective for web applications with many concurrent connections.
  • --bind 0.0.0.0:8000: Binds Gunicorn to all network interfaces on port 8000. Nginx will proxy to this.
  • --threads 2: Specifies the number of threads per worker process. Useful for I/O-bound tasks.
  • --timeout 120: Sets the worker timeout. Increase if your application has long-running requests.

For production deployments, consider using a process manager like systemd or supervisor to manage Gunicorn processes. Ensure Gunicorn is configured to listen on a private IP address within your VPC, and Nginx is configured to proxy to it.

Tuning PHP-FPM (PHP Applications)

If your Shopify backend is built with PHP, PHP-FPM (FastCGI Process Manager) is the standard way to interface with Nginx. Its performance is governed by the process manager settings.

PHP-FPM Pool Configuration

The configuration for PHP-FPM pools is typically found in /etc/php/[version]/fpm/pool.d/www.conf. The pm (process manager) setting is crucial.

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

; Choose one of the following process management strategies:
; pm = static
; pm = dynamic
pm = ondemand

; If using 'static' or 'dynamic'
; pm.max_children = 50       ; Max number of child processes at any time
; pm.start_servers = 5       ; Number of servers started when pm is dynamic
; pm.min_spare_servers = 2   ; Min number of idle servers when pm is dynamic
; pm.max_spare_servers = 8   ; Max number of idle servers when pm is dynamic

; If using 'ondemand'
pm.max_children = 100      ; Max number of child processes at any time
pm.max_requests = 500      ; Max requests per child process before respawning

; Other important settings
request_terminate_timeout = 120s
pm.process_idle_timeout = 10s ; Only for 'ondemand'

; For performance tuning, consider increasing these if your application is memory intensive
; pm.max_memory = 256M

; For debugging, you might want to disable opcache or set logging levels higher
; opcache.enable=1
; opcache.memory_consumption=128
; opcache.interned_strings_buffer=16
; opcache.max_accelerated_files=10000
; opcache.revalidate_freq=0
; opcache.validate_timestamps=0 ; Set to 1 in development
; opcache.fast_shutdown=1

; Error logging
; log_level = notice
; error_log = /var/log/php/php7.4-fpm.log

PHP-FPM Tuning Parameters:

  • pm = ondemand: This is often a good choice for applications with variable traffic. It starts child processes only when needed and shuts them down when idle, saving resources. For consistently high traffic, dynamic or static might be better.
  • pm.max_children: The absolute maximum number of child processes that will be created. This is a critical setting to prevent your server from running out of memory. Calculate this based on your server’s RAM and the average memory usage per PHP process.
  • pm.max_requests: Setting this to a reasonable value (e.g., 500-1000) helps prevent memory leaks by respawning child processes after they’ve handled a certain number of requests.
  • request_terminate_timeout: Sets the maximum time a script can run before being terminated. Essential for preventing runaway scripts.
  • opcache directives: Ensure OPcache is enabled and configured with sufficient memory (opcache.memory_consumption) and a good number of accelerated files (opcache.max_accelerated_files). Disabling timestamp validation (opcache.validate_timestamps=0) in production can significantly boost performance, but requires a deployment process that clears OPcache.

Elasticsearch Performance Tuning on AWS

For Shopify applications that leverage Elasticsearch for search or analytics, optimizing its performance is crucial. This involves JVM heap settings, shard allocation, and indexing strategies.

JVM Heap Size Configuration

Elasticsearch runs on the JVM. Allocating an appropriate heap size is vital. The general recommendation is to set Xms (initial heap size) and Xmx (maximum heap size) to the same value, and no more than 50% of the system’s total RAM, and not exceeding 30-32GB.

Edit jvm.options (typically found in /etc/elasticsearch/jvm.options or similar):

-Xms4g
-Xmx4g
# ... other JVM options

Note: If running Elasticsearch on EC2 instances, consider using instance types optimized for memory (e.g., `r5` or `i3` families) and EBS volumes optimized for I/O (e.g., `gp3` or `io1`). For production, AWS Elasticsearch Service (now Amazon OpenSearch Service) is often a more managed and scalable solution.

Shard Allocation and Indexing Strategies

The number and size of shards significantly impact search performance and cluster stability. Aim for shards between 10GB and 50GB.

Dynamic Shard Allocation:

PUT _cluster/settings
{
  "persistent" : {
    "cluster.routing.allocation.enable" : "all"
  }
}

Index Settings for Write Performance:

When indexing large amounts of data, consider temporarily increasing the number of replicas to 0 and then increasing it back after indexing is complete. Also, adjust refresh intervals.

# Temporarily disable replicas during bulk indexing
PUT my_index/_settings
{
  "index" : {
    "number_of_replicas" : 0
  }
}

# Increase refresh interval (e.g., to 60 seconds)
PUT my_index/_settings
{
  "index" : {
    "refresh_interval" : "60s"
  }
}

# Perform your bulk indexing here...

# Re-enable replicas and reset refresh interval
PUT my_index/_settings
{
  "index" : {
    "number_of_replicas" : 1,
    "refresh_interval" : "1s"
  }
}

Force Merge for Read Performance:

After indexing is complete and you no longer need to index into an index, you can force merge segments to optimize for search performance. This is a resource-intensive operation and should be done during off-peak hours.

POST my_index/_forcemerge?max_num_segments=1

Monitoring and Diagnostics

Continuous monitoring is key to maintaining optimal performance. Utilize AWS CloudWatch for EC2 metrics (CPU utilization, network I/O, disk I/O), ELB metrics, and RDS metrics. For application-level monitoring:

  • Nginx: Monitor stub_status for active connections, requests per second, and error rates. Use Nginx’s error logs for troubleshooting.
  • Gunicorn/PHP-FPM: Monitor process counts, memory usage, and request latency. Gunicorn has built-in logging; PHP-FPM logs can be configured for detailed error reporting.
  • Elasticsearch: Monitor cluster health (green, yellow, red), node status, JVM heap usage, CPU utilization, indexing rate, and search latency. Use Elasticsearch’s monitoring APIs and Kibana’s Stack Monitoring.

For deep dives, consider tools like Prometheus with Grafana for metrics aggregation and visualization, and the ELK stack (Elasticsearch, Logstash, Kibana) for centralized logging.

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 indexing lock conflicts and high CPU during bulk stock updates on DigitalOcean Servers
  • How to Debug and Fix memory leaks and socket exhaustion in daemon processes in Modern C++ Applications
  • Infrastructure as Code: Provisioning Secure PHP Clusters on DigitalOcean Using Terraform
  • 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

Copyright © 2026 · Vinay Vengala