• 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 Linode for Magento 2

The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and PostgreSQL on Linode for Magento 2

Nginx Configuration for Magento 2 Performance

Optimizing Nginx is paramount for serving Magento 2 efficiently. We’ll focus on caching, worker processes, and static file handling.

Worker Processes and Connections

The worker_processes directive should ideally be set to the number of CPU cores available on your server. This allows Nginx to utilize all available processing power. The worker_connections directive 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 traffic.

Example Nginx Configuration Snippet

worker_processes auto; # Or set to the number of CPU cores
events {
    worker_connections 4096; # Adjust based on RAM and traffic
    multi_accept on;
}

http {
    # ... other http configurations ...
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off; # Crucial for security and performance
    # ... rest of http block ...
}

Caching Strategies

Leveraging Nginx’s built-in FastCGI cache for PHP-FPM output and browser caching for static assets significantly reduces server load. For Magento 2, it’s also essential to configure Nginx to bypass the cache for dynamic requests (e.g., AJAX calls, logged-in users, checkout process).

FastCGI Caching for PHP-FPM

This requires defining a cache zone and then applying it to your PHP-FPM location. Ensure your fastcgi_cache_path is on a fast storage medium, preferably an SSD.

http {
    # ... other http configurations ...

    fastcgi_cache_path /var/cache/nginx/magento levels=1:2 keys_zone=magento_cache:100m inactive=60m;
    fastcgi_temp_path /var/tmp/nginx/fastcgi_temp;

    # ... rest of http block ...
}

server {
    # ... server configurations ...

    location ~ \.php$ {
        # ... fastcgi_pass and other php settings ...

        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;
        add_header X-FastCGI-Cache $upstream_cache_status; # Useful for debugging

        # Bypass cache for specific requests
        if ($http_cookie ~* "PHPSESSID|wordpress_logged_in|mage-cache-sessid") {
            fastcgi_cache_bypass 1;
            fastcgi_no_cache 1;
        }
        if ($request_method = POST) {
            fastcgi_cache_bypass 1;
            fastcgi_no_cache 1;
        }
        if ($request_uri ~* "(admin|checkout|customer)") {
            fastcgi_cache_bypass 1;
            fastcgi_no_cache 1;
        }
    }

    # ... rest of server block ...
}

Browser Caching for Static Assets

Configure long expiry times for static assets like CSS, JS, and images. Use ETags to ensure efficient cache validation.

location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
    expires 365d;
    add_header Cache-Control "public, immutable";
    access_log off;
    log_not_found off;
    etag on;
}

Gunicorn/PHP-FPM Tuning for Magento 2

The choice between Gunicorn (for Python-based applications, though less common for Magento 2 directly) and PHP-FPM (for PHP applications like Magento 2) dictates the tuning parameters. We’ll focus on PHP-FPM as it’s the standard for Magento 2.

PHP-FPM Configuration

The php-fpm.conf and pool configuration files (typically in /etc/php/[version]/fpm/pool.d/www.conf) are critical. The pm (process manager) setting is key. For busy sites, a dynamic or ondemand approach can save resources, but a static approach might offer more consistent performance under heavy load if you have sufficient RAM.

Process Manager (pm) Settings

  • pm = dynamic: This is often a good balance. It starts with a minimum number of processes and spawns more as needed, up to a maximum.
  • pm = ondemand: Processes are spawned only when a request arrives and are killed after a certain idle period. This saves memory but can introduce latency for the first request after an idle period.
  • pm = static: A fixed number of processes are kept running at all times. This offers the most predictable performance but can be resource-intensive.

Tuning Directives (for pm = dynamic)

; /etc/php/[version]/fpm/pool.d/www.conf

; Choose one of: static, dynamic, ondemand
pm = dynamic

; For pm = dynamic:
pm.max_children = 100      ; Maximum number of children that can be alive at the same time.
pm.start_servers = 5       ; Number of children created at startup.
pm.min_spare_servers = 5   ; Number of children to maintain in the idle pool.
pm.max_spare_servers = 15  ; Number of children to maintain in the idle pool.
pm.max_requests = 500      ; Maximum number of requests each child process should serve. Set to 0 for no limit.

; For pm = ondemand:
; pm.max_children = 50
; pm.max_requests = 500
; pm.process_idle_timeout = 10s ; The timeout after which an idle process will be killed.

; For pm = static:
; pm.max_children = 50

; Other important settings:
request_terminate_timeout = 60s ; Timeout for script execution. Magento can have long-running scripts.
; rlimit_files = 10240 ; Increase open file limits if needed
; rlimit_nofile = 10240 ; Increase open file limits if needed

Tuning Strategy: Start with pm = dynamic. Monitor your server’s CPU and RAM usage. If you see spikes in CPU under load, you might need to increase pm.max_children or switch to pm = static if you have ample RAM. If memory is a concern, pm = ondemand might be better, but monitor latency.

OpCache Configuration

OpCache is essential for PHP performance. Ensure it’s enabled and properly configured in your php.ini file.

; /etc/php/[version]/fpm/php.ini (and cli/apache2 if applicable)

[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256 ; Adjust based on your application's memory needs and server RAM. 256MB is a good starting point for Magento.
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000 ; Increase if you have a very large codebase.
opcache.revalidate_freq=2 ; How often to check for file updates (in seconds). Set to 0 in production if you manually clear cache after deploys.
opcache.validate_timestamps=1 ; Set to 0 in production for maximum performance if you have a robust deployment process.
opcache.save_comments=1
opcache.load_comments=1
opcache.enable_file_override=0
opcache.fast_shutdown=0
opcache.optimization_level=0xFFFFFFFF ; Enable all optimizations

PostgreSQL Tuning for Magento 2

PostgreSQL performance is critical for Magento 2’s database operations. Key areas include memory allocation, connection pooling, and query optimization.

Key PostgreSQL Configuration Parameters

These parameters are typically found in postgresql.conf. The values should be tuned based on your server’s RAM and workload. A common recommendation is to allocate 25-50% of your server’s RAM to PostgreSQL, but this is highly dependent on other services running on the same server.

# postgresql.conf

# Memory Allocation
shared_buffers = 4GB             ; Typically 25% of total RAM, but can be up to 40% for dedicated DB servers.
                                 ; For a 16GB Linode, 4GB is a reasonable start.
work_mem = 64MB                  ; Memory for internal sort operations and hash tables.
                                 ; Adjust based on complex queries and number of connections.
maintenance_work_mem = 256MB     ; Memory for vacuum, index creation, etc.
                                 ; Higher values speed up maintenance tasks.

# Connection Management
max_connections = 100            ; Number of concurrent connections. Magento can be chatty.
                                 ; Adjust based on your application's needs and server resources.
                                 ; Consider using a connection pooler like PgBouncer for very high loads.
shared_preload_libraries = 'pg_stat_statements' ; Essential for query analysis.

# Checkpointing
checkpoint_completion_target = 0.9 ; Spreads checkpoints over time, reducing I/O spikes.
max_wal_size = 4GB               ; Maximum size of WAL files before a checkpoint.
min_wal_size = 1GB               ; Minimum size of WAL files.

# Query Planning
random_page_cost = 1.1           ; Lowering this can make the planner favor sequential scans more.
                                 ; Default is 4.0. Adjust based on disk I/O performance.
seq_page_cost = 1.0              ; Default is 1.0.

# Logging (for debugging and analysis)
log_statement = 'ddl'            ; Log DDL statements. Consider 'all' for debugging, but it's verbose.
log_min_duration_statement = 250 ; Log statements longer than 250ms. Crucial for identifying slow queries.
log_checkpoints = on
log_connections = on
log_disconnections = on

Connection Pooling with PgBouncer

For high-traffic Magento 2 sites, managing a large number of direct PostgreSQL connections can be a bottleneck. PgBouncer acts as a lightweight connection pooler, significantly reducing the overhead of establishing and tearing down connections.

PgBouncer Configuration (pgbouncer.ini)

[databases]
# Format: database_name = connection_string
# Example:
# mydb = host=127.0.0.1 port=5432 dbname=mydb user=myuser password=mypass

[pgbouncer]
; Listen address and port
listen_addr = 127.0.0.1
listen_port = 6432

; Pool mode: session, transaction, or statement
; 'transaction' is generally recommended for Magento 2.
pool_mode = transaction

; Maximum number of clients that can connect to PgBouncer.
max_client_conn = 2000

; Maximum number of server connections for each database.
default_pool_size = 50

; Minimum number of server connections for each database.
min_pool_size = 5

; Maximum number of server connections to keep open per pool.
; If this is 0, then the pool size is unlimited.
pool_size_auto_growth = 0

; Connection timeout in seconds.
; server_connect_timeout = 15

; Authentication method. 'md5' is common.
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt

; Log level. 'info' is good for monitoring.
log_level = info

; Stats database (optional, for monitoring)
stats_period = 60
stats_file = /var/log/pgbouncer/stats.log

PgBouncer Userlist (userlist.txt)

# Format: "database" "user" "password"
# Example:
# "magento_db" "magento_user" "secure_password"

After configuring PgBouncer, you’ll need to update your Magento 2 app/etc/env.php to point to the PgBouncer port (e.g., 6432) instead of the direct PostgreSQL port (5432).

Query Optimization and Indexing

Regularly analyze slow queries using pg_stat_statements and ensure your Magento 2 database schema is properly indexed. Magento’s EAV model can lead to complex queries, so indexing is crucial.

Enabling and Querying pg_stat_statements

Ensure shared_preload_libraries = 'pg_stat_statements' is set in postgresql.conf and restart PostgreSQL. Then, create the extension in your Magento database:

-- Connect to your Magento database
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

To find the top 10 slowest queries:

SELECT
    query,
    calls,
    total_exec_time,
    mean_exec_time,
    rows
FROM
    pg_stat_statements
ORDER BY
    mean_exec_time DESC
LIMIT 10;

Use this information to identify queries that need optimization, potentially by adding custom indexes or refactoring Magento code.

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