🚀 NGINX Performance Optimization Guide

Section 11 - Advanced Configuration & Tuning

📋 Introduction

This comprehensive guide covers advanced NGINX configuration techniques to optimize performance, security, and resource management. We'll explore worker processes, timeout configurations, compression settings, caching mechanisms, and integration with MariaDB and PHP-FPM.

📚 Documentation Reference: For complete directive documentation, visit http://nginx.org/en/docs/dirindex.html

🔧 Initial Configuration Setup

Creating the Includes Directory

First, we'll create a dedicated directory for modular configuration files:

cd /etc/nginx/
sudo mkdir includes/
ls

Backing Up the Configuration

⚠️ Important: Always create a backup before modifying configuration files!
cd /etc/nginx
sudo cp nginx.conf nginx.conf.bak
sudo nano nginx.conf

⚙️ Main Context Configuration

The main context directives control global NGINX behavior and worker process settings.

worker_rlimit_nofile 30000;
worker_priority -10;
timer_resolution 100ms;
pcre_jit on;
Directive Value Description
worker_rlimit_nofile 30000 Maximum number of open files per worker process
worker_priority -10 Worker process priority (negative = higher priority)
timer_resolution 100ms Reduces timer resolution to improve performance
pcre_jit on Enables Just-In-Time compilation for regular expressions

🔄 Events Context Configuration

The events context controls how NGINX handles connections at the network level.

worker_connections 4096;
accept_mutex on;
accept_mutex_delay 200ms;
use epoll;

Connection Processing Flow

Client Request
Accept Mutex
Worker Process
epoll Handler
Response
Directive Value Description
worker_connections 4096 Maximum simultaneous connections per worker
accept_mutex on Serializes accept() calls to prevent thundering herd
accept_mutex_delay 200ms Delay before attempting to acquire mutex again
use epoll Uses epoll for efficient event processing (Linux)

📝 Creating Modular Configuration Files

We'll create separate configuration files for better organization and maintainability:

cd /etc/nginx/includes/
sudo touch basic_settings.conf buffers.conf timeouts.conf file_handle_cache.conf gzip.conf brotli.conf
ls -l

1. Basic Settings Configuration

File: basic_settings.conf

##
# BASIC SETTINGS
## 
charset utf-8;
sendfile on;
sendfile_max_chunk 512k;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
more_clear_headers 'Server';
more_clear_headers 'X-Powered';
server_name_in_redirect off;
server_names_hash_bucket_size 64;
variables_hash_max_size 2048;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
Key Optimizations:
  • sendfile on - Uses kernel sendfile() for efficient file transfers
  • tcp_nopush on - Optimizes packet transmission
  • tcp_nodelay on - Disables Nagle's algorithm for low latency
  • server_tokens off - Hides NGINX version for security

2. Buffer Settings Configuration

File: buffers.conf

##
# BUFFERS
## 
client_body_buffer_size 256k;
client_body_in_file_only off;
client_header_buffer_size 64k;
client_max_body_size 100m;
connection_pool_size 512;
directio 4m;
ignore_invalid_headers on;
large_client_header_buffers 8 64k;
output_buffers 8 256k;
postpone_output 1460;
request_pool_size 32k;
⚠️ Note: The client_max_body_size is set to 100m to allow theme and plugin uploads. Reduce to 8m after initial site setup for security.

3. Timeout Configuration

File: timeouts.conf

##
# TIMEOUTS
## 
keepalive_timeout 5;
keepalive_requests 500;
lingering_time 20s;
lingering_timeout 5s;
keepalive_disable msie6;
reset_timedout_connection on;
send_timeout 15s;
client_header_timeout 8s;
client_body_timeout 10s;

Timeout Types in NGINX

Client Timeouts
client_body_timeout
client_header_timeout
Keepalive Timeouts
keepalive_timeout
keepalive_requests
FastCGI Timeouts
fastcgi_connect_timeout
fastcgi_send_timeout
Connection Timeouts
send_timeout
lingering_timeout
Understanding Keepalive:

Keepalive allows multiple HTTP requests to reuse the same TCP connection, avoiding the overhead of establishing new connections. This is particularly beneficial for applications making many small requests.

  • keepalive_timeout 5 - Connection stays open for 5 seconds after last request
  • keepalive_requests 500 - Maximum 500 requests per connection
  • Benefits: Reduced latency, lower CPU usage, fewer socket operations

4. Gzip Compression Configuration

File: gzip.conf

##
# GZIP
## 
gzip on;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
gzip_static on;
gzip_min_length 1400;
gzip_buffers 32 8k;
gzip_http_version 1.0;
gzip_comp_level 5;
gzip_proxied any;
gzip_types text/plain text/css text/xml application/javascript application/x-javascript application/xml application/xml+rss application/ecmascript application/json image/svg+xml;
✅ Gzip Benefits:
  • Reduces bandwidth usage by 50-70%
  • Faster page load times
  • Lower data transfer costs
  • Improved user experience
Compression Level CPU Usage Compression Ratio Recommended Use
1-3 Low Basic High-traffic sites
4-6 Medium Good Balanced performance
7-9 High Excellent Low-traffic sites

5. Brotli Compression Configuration

File: brotli.conf

##
# BROTLI
## 
brotli on;
brotli_comp_level 6;
brotli_static on;
brotli_types application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml;
🆚 Brotli vs Gzip:

Brotli is a modern compression algorithm developed by Google that typically achieves 15-25% better compression than gzip, especially for text content.

  • Compression: Brotli provides better compression ratios
  • Speed: Gzip is faster at compression
  • Support: Brotli requires modern browsers
  • Best Practice: Enable both for maximum compatibility

6. File Handle Cache Configuration

File: file_handle_cache.conf

##
# FILE HANDLE CACHE
## 
open_file_cache max=50000 inactive=60s;
open_file_cache_valid 120s;
open_file_cache_min_uses 2;
open_file_cache_errors off;

File Handle Cache Operation

File Request
Check Cache
Cache Hit?
Yes: Return metadata
No: Read from disk
Serve File
Performance Impact:

The file handle cache can dramatically improve performance by caching file metadata (size, modification time, permissions) in memory, reducing filesystem access overhead.

🔗 Integrating Configuration Files

Now we'll include all our modular configuration files in the main nginx.conf:

cd /etc/nginx
sudo nano nginx.conf

HTTP Context Structure

Remove the old directives and add includes:

http {
    ##
    # Basic Settings
    ##
    include /etc/nginx/includes/basic_settings.conf;

    ##
    # Gzip and Brotli Settings
    ##
    include /etc/nginx/includes/gzip.conf;
    include /etc/nginx/includes/brotli.conf;

    ##
    # Buffer Settings
    ##
    include /etc/nginx/includes/buffers.conf;

    ##
    # Timeout Settings
    ##
    include /etc/nginx/includes/timeouts.conf;

    ##
    # File Handle Cache Settings
    ##
    include /etc/nginx/includes/file_handle_cache.conf;

    ##
    # Virtual Host Configs
    ##
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

✅ Testing and Validating Configuration

Configuration Syntax Testing

Always test your configuration before applying changes:

sudo nginx -t
Expected Output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Common Errors and Solutions

Error Message Cause Solution
Invalid number of arguments Missing semicolon Add semicolon at end of directive
No such file or directory Incorrect file path or typo Verify path with ls command
Unexpected token Syntax error in directive Check directive syntax in documentation

Debugging with Line Numbers

Use the -L flag to open files with line numbers:

sudo nano -L /etc/nginx/nginx.conf

Applying Changes

⚠️ Important: Reload vs Restart
  • Reload: Gracefully applies changes without dropping connections
  • Restart: Stops all processes and restarts (drops active connections)
  • Best Practice: Always use reload in production
sudo systemctl reload nginx

📊 Managing Resource Limits

Checking NGINX Open File Limits

Determine the NGINX worker process ID and verify limits:

ps aux | grep www-data

View the limits for a specific process (replace XXX with actual PID):

cat /proc/XXX/limits

Adjusting Open File Limits

Update the worker file limit in nginx.conf:

worker_rlimit_nofile 45000;

Test and reload:

sudo nginx -t
sudo systemctl reload nginx

Verifying New Limits

Get the new PID and verify:

ps aux | grep www-data
cat /proc/XXX/limits

⚡ Creating Useful BASH Aliases

Aliases improve workflow efficiency by creating shortcuts for common commands:

cd ~
nano .bash_aliases

System Maintenance Aliases

alias server_update='sudo apt update && sudo apt upgrade && sudo apt autoremove'

NGINX Management Aliases

alias ngt='sudo nginx -t'
alias ngr='sudo systemctl reload nginx'
alias fpmr='sudo systemctl restart php8.3-fpm'
alias ngsa='cd /etc/nginx/sites-available/ && ls'
alias ngin='cd /etc/nginx/includes/ && ls'

Activating Aliases

Switch to your user to load the new aliases:

su andrew
Usage Examples:
  • ngt - Test NGINX configuration
  • ngr - Reload NGINX
  • fpmr - Restart PHP-FPM
  • ngsa - Navigate to sites-available
  • ngin - Navigate to includes directory

🗄️ MariaDB Optimization

Initial Security Setup

sudo mysql_secure_installation

Performance Schema Configuration

Navigate to MariaDB configuration directory:

cd /etc/mysql/mariadb.conf.d/
sudo cp 50-server.cnf 50-server.cnf.bak
sudo nano 50-server.cnf

Enabling Performance Schema

# Performance Schema
performance_schema=ON
performance-schema-instrument='stage/%=ON'
performance-schema-consumer-events-stages-current=ON
performance-schema-consumer-events-stages-history=ON
performance-schema-consumer-events-stages-history-long=ON

Additional Optimizations

Skip Name Resolution

skip-name-resolve

Binary Log Retention

expire_logs_days = 3
Why skip-name-resolve?

This directive prevents MariaDB from performing DNS lookups on connecting clients, which can significantly improve connection times, especially if DNS is slow or unavailable.

InnoDB Buffer Pool Configuration

Memory Allocation for 1GB Server

Component Allocation Percentage
InnoDB Buffer Pool 800M 80%
InnoDB Log Files 200M 20%
System & Other ~200M Remaining

For a 1GB RAM server:

innodb_buffer_pool_size = 800M
innodb_log_file_size = 200M
⚠️ Important: Stop MariaDB before changing innodb_log_file_size as it requires recreation of log files.
sudo systemctl stop mariadb
sudo nano 50-server.cnf
# Add the directives above
sudo systemctl start mariadb

MariaDB Aliases

nano ~/.bash_aliases

alias mariare='sudo systemctl restart mariadb'

Using MySQLTuner

MySQLTuner analyzes your MariaDB performance and provides recommendations:

cd ~
mkdir MySQLTuner/
cd MySQLTuner/
wget http://mysqltuner.pl/ -O mysqltuner.pl
chmod +x mysqltuner.pl
sudo ./mysqltuner.pl

Setting MariaDB Open File Limits

Create systemd override directory:

cd /etc/systemd/system/
sudo mkdir mariadb.service.d/
cd mariadb.service.d/
sudo nano limits.conf

Add the following content:

[Service]
LimitNOFILE=40000

Apply changes:

sudo systemctl daemon-reload
sudo systemctl restart mariadb

Verifying MariaDB Limits

ps aux | grep mysql
cat /proc/XXX/limits

🐘 PHP-FPM Optimization

Creating Server Override Configuration

cd /etc/php/8.3/fpm/conf.d/
sudo nano server_override.ini

Security and Performance Settings

# HARDENING PHP
allow_url_fopen = Off
cgi.fix_pathinfo=0
expose_php = Off

# OPTIMIZE PHP
upload_max_filesize = 100M
post_max_size = 125M
max_input_vars = 3000
memory_limit = 256M
Directive Value Purpose
allow_url_fopen Off Security - prevents remote file inclusion
cgi.fix_pathinfo 0 Security - prevents path disclosure attacks
expose_php Off Security - hides PHP version
upload_max_filesize 100M Allows large file uploads
post_max_size 125M Must be larger than upload_max_filesize
memory_limit 256M Maximum memory per script

Configuring PHP-FPM Resource Limits

Open PHP-FPM configuration:

cd /etc/php/8.3/fpm/
sudo cp php-fpm.conf php-fpm.conf.bak
sudo nano php-fpm.conf

Find and update the resource limits:

rlimit_files = 32768
rlimit_core = unlimited

Applying PHP-FPM Changes

sudo systemctl reload nginx
sudo systemctl restart php8.3-fpm

Verifying PHP-FPM Limits

ps aux | grep php-fpm
cat /proc/XXX/limits
✅ Verification Checklist:
  • NGINX worker limits increased
  • MariaDB file limits configured
  • PHP-FPM resource limits set
  • All services reloaded successfully

🎯 Best Practices Summary

Configuration Management

  • Always backup: Create .bak files before editing
  • Test first: Run nginx -t before applying changes
  • Use reload: Never use restart in production
  • Modular configs: Keep configurations organized in separate files
  • Document changes: Add comments explaining custom settings

Performance Optimization

  • Enable compression: Use both gzip and Brotli
  • Tune buffers: Adjust based on your traffic patterns
  • Set proper timeouts: Balance performance and resource usage
  • Use caching: Enable file handle cache for static content
  • Monitor limits: Regularly check and adjust resource limits

Security Considerations

  • Hide server version information
  • Disable unnecessary PHP functions
  • Set appropriate file upload limits
  • Use skip-name-resolve in MariaDB
  • Regularly update all software components

🔍 Troubleshooting Guide

Common Issues and Solutions

Issue Diagnosis Solution
Configuration test fails Check error message and line number Open file with nano -L, fix syntax error
Service won't start Check logs: journalctl -xe Review error logs, restore from backup if needed
High CPU usage Check worker processes and compression level Reduce gzip/brotli compression levels
Connection errors Check worker_connections and limits Increase worker_connections and file limits
Slow database Run MySQLTuner Adjust InnoDB buffer pool size

Useful Diagnostic Commands

# Check NGINX status
sudo systemctl status nginx

# Check error logs
sudo tail -f /var/log/nginx/error.log

# Check MariaDB status
sudo systemctl status mariadb

# Check PHP-FPM status
sudo systemctl status php8.3-fpm

# Monitor system resources
htop

# Check listening ports
sudo netstat -tulpn | grep LISTEN

📖 Quick Reference

Essential Commands

NGINX Commands

  • sudo nginx -t - Test configuration
  • sudo systemctl reload nginx - Reload configuration
  • sudo systemctl restart nginx - Restart service
  • sudo systemctl status nginx - Check status

MariaDB Commands

  • sudo mysql - Connect to MariaDB
  • sudo systemctl restart mariadb - Restart service
  • SHOW VARIABLES LIKE '%var_name%' - Check variable

PHP-FPM Commands

  • sudo systemctl restart php8.3-fpm - Restart PHP-FPM
  • php -v - Check PHP version
  • php -m - List installed modules

🎓 Conclusion

This comprehensive guide has covered the essential aspects of NGINX performance optimization, including:

  • Worker process and event handling configuration
  • Timeout and keepalive optimization
  • Compression with gzip and Brotli
  • File handle caching for improved performance
  • MariaDB tuning and security hardening
  • PHP-FPM optimization and resource management
  • Workflow enhancement with BASH aliases

Remember to always test configurations in a development environment before applying to production, and maintain regular backups of your configuration files.

Next Steps:

Continue to monitor your server performance using tools like MySQLTuner, and adjust settings based on your specific workload and traffic patterns. Regular optimization reviews will help maintain peak performance.