Introduction
Server configuration and monitoring are critical for Laravel application performance in production. While application-level optimization improves code efficiency, server-level optimization ensures your infrastructure delivers maximum throughput, minimal latency, and reliable uptime.
A well-configured server can handle 10x more traffic than a poorly configured one running the same application. Proper monitoring enables proactive issue detection, performance troubleshooting, and capacity planning before problems impact users.
In this comprehensive guide, we'll optimize server infrastructure for Laravel 12 production deployments, covering PHP-FPM configuration, web server tuning, database optimization, monitoring tools, and best practices for maintaining high-performance, reliable Laravel applications.
PHP-FPM Optimization
1. Process Manager Configuration
Edit /etc/php/8.3/fpm/pool.d/www.conf:
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
; Process Management
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500
; Performance Settings
pm.process_idle_timeout = 10s
request_terminate_timeout = 300
rlimit_files = 65536
rlimit_core = 0
; Status page
pm.status_path = /fpm-status
ping.path = /fpm-ping
; Slow log for debugging
slowlog = /var/log/php8.3-fpm-slow.log
request_slowlog_timeout = 5s
; Security
php_admin_value[error_log] = /var/log/php8.3-fpm.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M
2. Calculate Optimal pm.max_children
# Formula: (Total RAM - RAM for other services) / Average PHP process memory
# Check average PHP process memory
ps aux | grep php-fpm | awk '{sum+=$6} END {print sum/NR/1024 " MB"}'
# Example calculation:
# Server RAM: 4GB (4096 MB)
# Reserved for system/MySQL/Redis: 1GB (1024 MB)
# Available for PHP: 3GB (3072 MB)
# Average PHP process: 50 MB
# Max children: 3072 / 50 = ~60
pm.max_children = 60
pm.start_servers = 15
pm.min_spare_servers = 10
pm.max_spare_servers = 25
3. PHP Configuration (php.ini)
Edit /etc/php/8.3/fpm/php.ini:
; Memory and Execution
memory_limit = 256M
max_execution_time = 300
max_input_time = 300
post_max_size = 50M
upload_max_filesize = 50M
; OPcache Configuration
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.save_comments=1
opcache.fast_shutdown=1
opcache.enable_cli=0
; Realpath Cache
realpath_cache_size=4096K
realpath_cache_ttl=600
; Session Configuration
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?database=2"
session.gc_maxlifetime = 7200
; Error Handling (Production)
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/error.log
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
; Security
expose_php = Off
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
Nginx Optimization
1. Main Nginx Configuration
Edit /etc/nginx/nginx.conf:
user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /run/nginx.pid;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
# Basic Settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30;
keepalive_requests 100;
types_hash_max_size 2048;
server_tokens off;
client_max_body_size 50M;
# Buffer Settings
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 16k;
# Timeouts
client_body_timeout 12;
client_header_timeout 12;
send_timeout 10;
# FastCGI Settings
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
# Gzip Compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/rss+xml font/truetype font/opentype
application/vnd.ms-fontobject image/svg+xml;
gzip_disable "msie6";
# Open File Cache
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# Logging
access_log /var/log/nginx/access.log combined buffer=32k;
error_log /var/log/nginx/error.log warn;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
2. Laravel Site Configuration
Create /etc/nginx/sites-available/laravel:
upstream php-fpm {
server unix:/run/php/php8.3-fpm.sock;
keepalive 16;
}
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
root /var/www/laravel/current/public;
index index.php index.html;
# SSL Configuration
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Static File Caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
log_not_found off;
}
# Laravel Routes
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
# PHP-FPM Configuration
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-fpm;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
# FastCGI Cache
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache LARAVEL;
fastcgi_cache_valid 200 60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
add_header X-Cache-Status $upstream_cache_status;
}
# Deny access to hidden files
location ~ /\.(?!well-known).* {
deny all;
}
# Rate Limiting
limit_req zone=laravel_limit burst=20 nodelay;
}
# Rate Limiting Zone
limit_req_zone $binary_remote_addr zone=laravel_limit:10m rate=10r/s;
# FastCGI Cache
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=LARAVEL:100m inactive=60m;
MySQL Optimization
1. MySQL Configuration
Edit /etc/mysql/mysql.conf.d/mysqld.cnf:
[mysqld]
# Basic Settings
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
datadir = /var/lib/mysql
# Performance Settings
max_connections = 200
max_allowed_packet = 64M
thread_cache_size = 50
table_open_cache = 4000
table_definition_cache = 2000
# InnoDB Settings
innodb_buffer_pool_size = 2G
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1
innodb_buffer_pool_instances = 4
# Query Cache (MySQL 5.7 only)
query_cache_type = 1
query_cache_size = 64M
query_cache_limit = 2M
# Temporary Tables
tmp_table_size = 64M
max_heap_table_size = 64M
# Logging
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow-query.log
long_query_time = 2
log_queries_not_using_indexes = 1
# Binary Logging
log_bin = /var/log/mysql/mysql-bin.log
binlog_expire_logs_seconds = 604800
max_binlog_size = 100M
2. MySQL Performance Tuning
# Install MySQLTuner
wget http://mysqltuner.pl/ -O mysqltuner.pl
chmod +x mysqltuner.pl
./mysqltuner.pl
# Check slow queries
mysqldumpslow -s t -t 10 /var/log/mysql/slow-query.log
# Optimize tables
mysqlcheck -o --all-databases -u root -p
# Analyze queries
EXPLAIN SELECT * FROM posts WHERE user_id = 1;
Redis Optimization
1. Redis Configuration
Edit /etc/redis/redis.conf:
# Memory Management
maxmemory 512mb
maxmemory-policy allkeys-lru
# Persistence
save 900 1
save 300 10
save 60 10000
# Performance
tcp-backlog 511
timeout 300
tcp-keepalive 60
databases 16
# Append Only File
appendonly yes
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# Slow Log
slowlog-log-slower-than 10000
slowlog-max-len 128
# Security
requirepass your_strong_password
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command CONFIG ""
2. Redis Monitoring
# Monitor Redis performance
redis-cli --stat
# Check memory usage
redis-cli INFO memory
# Monitor slow commands
redis-cli SLOWLOG GET 10
# Check connected clients
redis-cli CLIENT LIST
System-Level Optimization
1. Kernel Parameters
Edit /etc/sysctl.conf:
# Network Optimization
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_tw_reuse = 1
# File Descriptors
fs.file-max = 2097152
fs.nr_open = 2097152
# Memory Management
vm.swappiness = 10
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
Apply changes:
sudo sysctl -p
2. Increase File Limits
Edit /etc/security/limits.conf:
* soft nofile 65535
* hard nofile 65535
www-data soft nofile 65535
www-data hard nofile 65535
Application Monitoring
1. Laravel Telescope
# Install Telescope
composer require laravel/telescope
php artisan telescope:install
php artisan migrate
# Publish configuration
php artisan vendor:publish --tag=telescope-config
Configure in config/telescope.php:
'enabled' => env('TELESCOPE_ENABLED', false),
'storage' => [
'database' => [
'connection' => env('DB_CONNECTION', 'mysql'),
'chunk' => 1000,
],
],
'watchers' => [
Watchers\QueryWatcher::class => [
'enabled' => true,
'slow' => 100, // Log queries over 100ms
],
Watchers\RequestWatcher::class => [
'enabled' => true,
'size_limit' => 64,
],
],
2. Laravel Pulse
# Install Pulse
composer require laravel/pulse
php artisan pulse:install
Configure in config/pulse.php:
'ingest' => [
'driver' => env('PULSE_INGEST_DRIVER', 'storage'),
'trim' => [
'keep' => '7 days',
],
],
'recorders' => [
Recorders\Servers::class => [
'server_name' => env('PULSE_SERVER_NAME', gethostname()),
'directories' => [
'/' => storage_path(),
],
],
Recorders\SlowQueries::class => [
'threshold' => 1000,
'sample_rate' => 1,
],
],
3. New Relic APM
# Install New Relic PHP agent
curl -Ls https://download.newrelic.com/php_agent/release/newrelic-php5-10.x.x-linux.tar.gz | tar -C /tmp -zx
cd /tmp/newrelic-php5-*
sudo ./newrelic-install install
# Configure New Relic
sudo nano /etc/php/8.3/fpm/conf.d/newrelic.ini
Configuration:
newrelic.enabled = true
newrelic.license = "YOUR_LICENSE_KEY"
newrelic.appname = "Laravel Production"
newrelic.framework = "laravel"
newrelic.framework.drupal.modules = false
newrelic.browser_monitoring.auto_instrument = true
Server Monitoring Tools
1. System Monitoring with Netdata
# Install Netdata
bash <(curl -Ss https://my-netdata.io/kickstart.sh)
# Access dashboard
http://your-server-ip:19999
2. Custom Health Check Endpoint
Create health check route:
// routes/web.php
Route::get('/health', function () {
$checks = [
'database' => checkDatabase(),
'redis' => checkRedis(),
'storage' => checkStorage(),
'queue' => checkQueue(),
];
$allHealthy = !in_array(false, $checks, true);
return response()->json([
'status' => $allHealthy ? 'healthy' : 'degraded',
'checks' => $checks,
'timestamp' => now()->toIso8601String(),
], $allHealthy ? 200 : 503);
});
function checkDatabase(): bool
{
try {
DB::connection()->getPdo();
return true;
} catch (\Exception $e) {
return false;
}
}
function checkRedis(): bool
{
try {
return Redis::ping() === true;
} catch (\Exception $e) {
return false;
}
}
function checkStorage(): bool
{
return is_writable(storage_path());
}
function checkQueue(): bool
{
try {
$size = Queue::size();
return $size < 10000; // Alert if queue too large
} catch (\Exception $e) {
return false;
}
}
3. Uptime Monitoring
Configure UptimeRobot or similar service to monitor:
# Endpoints to monitor
https://example.com/health
https://example.com (main site)
https://example.com/api/health (API health)
# Alert thresholds
- Response time > 3000ms
- HTTP status ≠ 200
- Down for > 5 minutes
Log Management
1. Centralized Logging
Configure Laravel logging:
// config/logging.php
'channels' => [
'production' => [
'driver' => 'stack',
'channels' => ['daily', 'slack'],
'ignore_exceptions' => false,
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => 14,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'Laravel Log',
'emoji' => ':boom:',
'level' => 'critical',
],
],
2. Log Rotation
Create /etc/logrotate.d/laravel:
/var/www/laravel/storage/logs/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
systemctl reload php8.3-fpm > /dev/null 2>&1
endscript
}
Performance Monitoring Dashboard
Create Custom Monitoring Dashboard
// app/Console/Commands/MonitorPerformance.php
class MonitorPerformance extends Command
{
protected $signature = 'monitor:performance';
public function handle()
{
$metrics = [
'memory' => $this->getMemoryUsage(),
'cpu' => $this->getCpuUsage(),
'disk' => $this->getDiskUsage(),
'queue_size' => Queue::size(),
'cache_hits' => $this->getCacheHitRate(),
'slow_queries' => $this->getSlowQueryCount(),
];
// Send to monitoring service
Http::post('https://metrics.example.com/api/metrics', $metrics);
// Alert if thresholds exceeded
if ($metrics['memory'] > 80) {
Mail::to('admin@example.com')->send(new HighMemoryAlert($metrics));
}
}
}
Automated Performance Testing
# Install Apache Bench
sudo apt-get install apache2-utils
# Run load test
ab -n 1000 -c 10 https://example.com/
# Install and use Siege
sudo apt-get install siege
siege -c 50 -t 1M https://example.com/
Conclusion
Optimizing Laravel server performance and implementing comprehensive monitoring ensures production applications remain fast, reliable, and scalable. By properly configuring PHP-FPM, Nginx, MySQL, Redis, and implementing robust monitoring tools, you create infrastructure that handles traffic efficiently while providing visibility into system health.
Key takeaways:
- Configure PHP-FPM with appropriate process management
- Optimize Nginx for high-performance request handling
- Tune MySQL and Redis for optimal database performance
- Implement system-level kernel optimizations
- Use monitoring tools like Telescope, Pulse, and New Relic
- Create health check endpoints for automated monitoring
- Set up centralized logging and alerting
- Perform regular performance testing
Need help optimizing your production infrastructure? NeedLaravelSite specializes in server optimization and performance monitoring. Contact us for expert Laravel development services.
Related Resources: