The Ultimate DevOps Playbook: Tuning Nginx, Gunicorn/FPM, and MySQL on OVH for Laravel
Nginx Configuration for Laravel on OVH
Optimizing Nginx is crucial for serving Laravel applications efficiently, especially on OVH infrastructure where resource allocation and network latency can be factors. We’ll focus on key directives that impact performance and security.
Worker Processes and Connections
The `worker_processes` directive controls how many worker processes Nginx spawns. Setting this to `auto` is generally recommended, allowing Nginx to determine the optimal number based on available CPU cores. The `worker_connections` directive sets 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 anticipated traffic load.
Edit your main Nginx configuration file (often `/etc/nginx/nginx.conf`):
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 4096; # Increased from 1024
}
Gzip Compression
Enabling Gzip compression significantly reduces the size of text-based assets (HTML, CSS, JS, JSON), leading to faster load times. Ensure it’s enabled and configured appropriately for common file types.
http {
# ... other http directives ...
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6; # Compression level (1-9)
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
# ... rest of http configuration ...
}
Caching and Keep-Alive
Leveraging browser caching and HTTP keep-alive can reduce server load and improve client-side performance. Set appropriate `Cache-Control` headers for static assets and configure `keepalive_timeout`.
server {
# ... other server directives ...
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
keepalive_timeout 65; # Default is 65, adjust if needed
keepalive_requests 1000; # Default is 1000, adjust if needed
# ... rest of server configuration ...
}
PHP-FPM Configuration for Laravel
When using PHP-FPM with Nginx for Laravel, tuning the PHP-FPM pool is critical. The `pm` (process manager) setting determines how PHP processes are managed. `dynamic` is a good balance for most applications, allowing the number of child processes to scale based on demand.
Process Manager Settings (pm)
Key directives for `pm` are `pm.max_children`, `pm.start_servers`, `pm.min_spare_servers`, and `pm.max_spare_servers`. These control the number of PHP worker processes. Setting these too high can exhaust server memory; too low can lead to request queuing.
Locate your PHP-FPM pool configuration file (e.g., `/etc/php/8.1/fpm/pool.d/www.conf` or similar, depending on your PHP version and OS distribution):
; For dynamic process management pm = dynamic pm.max_children = 100 ; Adjust based on available RAM. (e.g., 100 processes * 20MB/process = 2GB RAM) pm.start_servers = 10 ; Initial number of children to start pm.min_spare_servers = 5 ; Minimum number of idle processes pm.max_spare_servers = 20 ; Maximum number of idle processes ; For static process management (if you have predictable load and ample RAM) ; pm = static ; pm.max_children = 150 ; Other important settings request_terminate_timeout = 60s ; Timeout for script execution pm.process_idle_timeout = 10s ; Timeout for idle processes to be killed
Tuning `pm.max_children`: A common rule of thumb is to calculate the maximum memory a single PHP-FPM process consumes (e.g., 20-30MB for a lean Laravel app, potentially more with heavy packages) and divide your total available RAM by this figure. Subtract a buffer for the OS and other services (like Nginx and MySQL). For example, on a 4GB RAM server, if each PHP process uses 30MB, `max_children` could be around `(4096MB – 1024MB buffer) / 30MB ≈ 102`. Start conservatively and monitor.
OPcache Configuration
OPcache is essential for PHP performance. It caches precompiled script bytecode in shared memory, eliminating the need to load and parse PHP scripts on every request. Ensure it’s enabled and tuned.
; In your php.ini file (e.g., /etc/php/8.1/fpm/php.ini) opcache.enable=1 opcache.enable_cli=1 ; Important for Artisan commands opcache.memory_consumption=128 ; MB, adjust based on your application's size and number of files opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 ; Number of files to cache. Increase if you have many PHP files. opcache.revalidate_freq=60 ; Check for file updates every 60 seconds (0 to disable, not recommended for production) opcache.validate_timestamps=1 ; Set to 0 in production for maximum performance if you have a deployment process that clears opcache opcache.save_comments=1 opcache.load_comments=1 opcache.enable_file_override=0 opcache.fast_shutdown=0
After making changes to PHP-FPM or OPcache, you must restart the respective services:
sudo systemctl restart php8.1-fpm # Adjust version as needed sudo systemctl restart nginx
MySQL Tuning for Laravel Applications
Database performance is often a bottleneck. Tuning MySQL, particularly its buffer pool and connection handling, can yield significant improvements for Laravel applications.
InnoDB Buffer Pool
The `innodb_buffer_pool_size` is the most critical MySQL setting. It caches InnoDB data and indexes. A common recommendation is to set it to 50-70% of your available RAM on a dedicated database server. On a shared server, be more conservative.
; In your my.cnf or my.ini file (e.g., /etc/mysql/mysql.conf.d/mysqld.cnf) [mysqld] innodb_buffer_pool_size = 2G ; Example for a server with 4GB RAM, adjust accordingly innodb_log_file_size = 256M ; Recommended to be at least 25% of innodb_buffer_pool_size innodb_flush_log_at_trx_commit = 1 ; For ACID compliance, set to 2 for better performance with slight risk on OS crash
Connection Handling
`max_connections` determines the maximum number of simultaneous client connections. Laravel applications can sometimes open more connections than expected due to ORM behavior or middleware. Monitor your actual usage and set this appropriately, but avoid setting it excessively high, as each connection consumes memory.
; In your my.cnf or my.ini file [mysqld] max_connections = 200 ; Adjust based on application needs and server resources thread_cache_size = 16 ; Cache threads for reuse
Query Cache (Deprecated/Removed)
Note: The MySQL query cache has been deprecated and removed in MySQL 8.0. If you are using an older version, it might be beneficial, but it often causes more contention than it solves in high-write environments. For modern setups, focus on other optimizations.
Other MySQL Tuning Parameters
Consider these for further tuning:
- `innodb_flush_method`: `O_DIRECT` can bypass OS caching and improve performance on some systems.
- `innodb_io_capacity`: Tune based on your disk I/O capabilities.
- `tmp_table_size` and `max_heap_table_size`: Control the size of in-memory temporary tables.
; In your my.cnf or my.ini file [mysqld] innodb_flush_method = O_DIRECT innodb_io_capacity = 2000 ; Adjust based on your storage (e.g., SSDs can handle higher values) tmp_table_size = 64M max_heap_table_size = 64M
After modifying MySQL configuration, restart the MySQL service:
sudo systemctl restart mysql # Or mysqld, depending on your OS
Laravel Specific Optimizations
Beyond infrastructure, Laravel itself offers tuning opportunities.
Configuration Caching
Cache your Laravel configuration to reduce the overhead of reading configuration files on each request.
php artisan config:cache
Important: If you run this command, ensure your configuration files are stable. Changes to configuration will not be reflected until you clear the cache (`php artisan config:clear`) and re-cache.
Route Caching
Similar to configuration, route definitions can be cached.
php artisan route:cache
Important: This is not suitable if you use route closures or dynamic route definitions. Clear with `php artisan route:clear`.
View Caching
Blade views are compiled and cached by default. Ensure this is enabled.
php artisan view:cache
Clear with `php artisan view:clear`.
Composer Autoloader Optimization
Optimize the Composer autoloader for faster class loading.
composer dump-autoload --optimize --no-dev
The `–no-dev` flag is crucial for production to exclude development dependencies.
Gunicorn/PHP-FPM Process Management (if using Gunicorn)
If your Laravel application is served via Gunicorn (often in a Python/WSGI context, though less common for pure PHP Laravel), the process management of Gunicorn itself needs tuning. However, for typical Laravel deployments on OVH using Nginx + PHP-FPM, the PHP-FPM tuning above is the primary focus. If you *are* using Gunicorn to proxy to PHP-FPM (e.g., via `php-fpm-socket`), the Gunicorn worker count should be set based on available CPU cores, and the PHP-FPM pool should be configured as detailed previously.
Monitoring and Iteration
Tuning is an iterative process. Continuously monitor your server’s performance using tools like:
htop/topfor CPU and memory usage.iotopfor disk I/O.netstat/ssfor network connections.- MySQL’s `SHOW GLOBAL STATUS;` and `SHOW ENGINE INNODB STATUS;`.
- PHP-FPM’s status page (if enabled).
- Application Performance Monitoring (APM) tools like New Relic, Datadog, or Sentry for deeper insights into application-level bottlenecks.
Adjust settings incrementally and observe the impact. What works best will depend on your specific Laravel application’s workload, traffic patterns, and the OVH instance type you are using.