Deployment & DevOps: Laravel Production Deployment Mastery
Deployment & DevOps: Laravel Production Deployment Mastery
Expert Guide by Alaa Amer – Professional Web Developer & Applications Designer
Master Laravel deployment and DevOps practices for production environments. Learn CI/CD, containerization, monitoring, scaling strategies, and enterprise-level infrastructure management.
Docker Compose for Production:
# docker-compose.prod.yml
version: "3.8"
services:
app:
build:
context: .
dockerfile: Dockerfile
target: production
image: laravel-app:latest
restart: unless-stopped
environment:
- APP_ENV=production
- APP_DEBUG=false
volumes:
- ./storage:/var/www/html/storage
- ./bootstrap/cache:/var/www/html/bootstrap/cache
networks:
- laravel-network
depends_on:
- database
- redis
deploy:
replicas: 3
resources:
limits:
cpus: "1"
memory: 512M
reservations:
cpus: "0.5"
memory: 256M
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./docker/nginx/ssl:/etc/ssl/certs
- ./storage/app/public:/var/www/html/storage/app/public
networks:
- laravel-network
depends_on:
- app
database:
image: mysql:8.0
restart: unless-stopped
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_USER: ${DB_USERNAME}
volumes:
- db-data:/var/lib/mysql
- ./docker/mysql/my.cnf:/etc/mysql/conf.d/custom.cnf
networks:
- laravel-network
command: --innodb-buffer-pool-size=1G --innodb-log-file-size=256M
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis-data:/data
- ./docker/redis/redis.conf:/etc/redis/redis.conf
networks:
- laravel-network
command: redis-server /etc/redis/redis.conf
queue-worker:
build:
context: .
dockerfile: Dockerfile
target: production
restart: unless-stopped
command: php artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
volumes:
- ./storage:/var/www/html/storage
networks:
- laravel-network
depends_on:
- database
- redis
deploy:
replicas: 3
scheduler:
build:
context: .
dockerfile: Dockerfile
target: production
restart: unless-stopped
command: php artisan schedule:work
volumes:
- ./storage:/var/www/html/storage
networks:
- laravel-network
depends_on:
- database
- redis
volumes:
db-data:
redis-data:
networks:
laravel-network:
driver: bridge
4️⃣ Monitoring & Observability
Application Monitoring Setup:
<?php
// app/Http/Middleware/ApplicationMonitoring.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\{Log, Cache, DB};
use Prometheus\CollectorRegistry;
use Prometheus\Storage\Redis;
class ApplicationMonitoring
{
protected $registry;
public function __construct()
{
$this->registry = new CollectorRegistry(new Redis());
}
public function handle(Request $request, Closure $next)
{
$startTime = microtime(true);
$response = $next($request);
$duration = microtime(true) - $startTime;
// Record metrics
$this->recordMetrics($request, $response, $duration);
return $response;
}
protected function recordMetrics(Request $request, $response, float $duration): void
{
// HTTP request duration
$histogram = $this->registry->getOrRegisterHistogram(
'laravel_app',
'http_request_duration_seconds',
'HTTP request duration in seconds',
['method', 'route', 'status_code'],
[0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
);
$histogram->observe(
$duration,
[
$request->method(),
$request->route()?->getName() ?? 'unknown',
(string) $response->getStatusCode()
]
);
// HTTP request count
$counter = $this->registry->getOrRegisterCounter(
'laravel_app',
'http_requests_total',
'Total number of HTTP requests',
['method', 'status_code']
);
$counter->inc([
$request->method(),
(string) $response->getStatusCode()
]);
// Database query count
if (DB::getQueryLog()) {
$queryGauge = $this->registry->getOrRegisterGauge(
'laravel_app',
'database_queries_per_request',
'Number of database queries per request'
);
$queryGauge->set(count(DB::getQueryLog()));
}
// Memory usage
$memoryGauge = $this->registry->getOrRegisterGauge(
'laravel_app',
'memory_usage_bytes',
'Memory usage in bytes'
);
$memoryGauge->set(memory_get_peak_usage(true));
}
}
// app/Console/Commands/MonitorSystem.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\{DB, Cache, Queue};
class MonitorSystem extends Command
{
protected $signature = 'monitor:system';
protected $description = 'Monitor system health and send alerts';
public function handle(): void
{
$this->info('Running system monitoring...');
$checks = [
'database' => $this->checkDatabase(),
'cache' => $this->checkCache(),
'queue' => $this->checkQueue(),
'storage' => $this->checkStorage(),
'memory' => $this->checkMemory(),
'logs' => $this->checkLogs()
];
$failures = array_filter($checks, fn($status) => !$status);
if (!empty($failures)) {
$this->sendAlert($failures);
$this->error('System health check failed: ' . implode(', ', array_keys($failures)));
} else {
$this->info('All system checks passed');
}
$this->recordHealthMetrics($checks);
}
protected function checkDatabase(): bool
{
try {
DB::select('SELECT 1');
// Check slow queries
$slowQueries = DB::select("
SELECT COUNT(*) as slow_count
FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 30
");
return $slowQueries[0]->slow_count < 5;
} catch (\Exception $e) {
Log::error('Database health check failed', ['error' => $e->getMessage()]);
return false;
}
}
protected function checkCache(): bool
{
try {
$testKey = 'health_check_' . time();
Cache::put($testKey, 'test', 60);
$result = Cache::get($testKey);
Cache::forget($testKey);
return $result === 'test';
} catch (\Exception $e) {
Log::error('Cache health check failed', ['error' => $e->getMessage()]);
return false;
}
}
protected function checkQueue(): bool
{
try {
// Check queue sizes
$queueSizes = [
'default' => Queue::size('default'),
'high' => Queue::size('high'),
'emails' => Queue::size('emails')
];
// Alert if any queue has too many jobs
return !collect($queueSizes)->contains(fn($size) => $size > 1000);
} catch (\Exception $e) {
Log::error('Queue health check failed', ['error' => $e->getMessage()]);
return false;
}
}
protected function checkStorage(): bool
{
$storagePath = storage_path();
$freeSpace = disk_free_space($storagePath);
$totalSpace = disk_total_space($storagePath);
$usagePercent = (($totalSpace - $freeSpace) / $totalSpace) * 100;
return $usagePercent < 90; // Alert if usage > 90%
}
protected function checkMemory(): bool
{
$memoryUsage = memory_get_usage(true);
$memoryLimit = $this->parseMemoryLimit(ini_get('memory_limit'));
$usagePercent = ($memoryUsage / $memoryLimit) * 100;
return $usagePercent < 90;
}
protected function checkLogs(): bool
{
$logPath = storage_path('logs/laravel.log');
if (!file_exists($logPath)) {
return true;
}
// Check for recent errors
$recentErrors = shell_exec("grep -c 'ERROR' $logPath | tail -1000");
return intval($recentErrors) < 50; // Alert if > 50 errors in last 1000 lines
}
protected function parseMemoryLimit(string $limit): int
{
$limit = trim($limit);
$last = strtolower($limit[strlen($limit) - 1]);
$limit = (int) $limit;
switch ($last) {
case 'g': $limit *= 1024 * 1024 * 1024; break;
case 'm': $limit *= 1024 * 1024; break;
case 'k': $limit *= 1024; break;
}
return $limit;
}
protected function sendAlert(array $failures): void
{
// Send to monitoring service (Slack, PagerDuty, etc.)
Log::critical('System health check failures detected', $failures);
// You could integrate with external services here
// Notification::route('slack', '#alerts')->notify(new SystemHealthAlert($failures));
}
protected function recordHealthMetrics(array $checks): void
{
foreach ($checks as $service => $status) {
Cache::put("health_check:{$service}", $status ? 1 : 0, 300);
}
}
}
Grafana Dashboard Configuration:
{
"dashboard": {
"title": "Laravel Application Metrics",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(laravel_app_http_requests_total[5m])",
"legendFormat": "{{method}} {{status_code}}"
}
]
},
{
"title": "Response Time",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(laravel_app_http_request_duration_seconds_bucket[5m]))",
"legendFormat": "95th percentile"
},
{
"expr": "histogram_quantile(0.50, rate(laravel_app_http_request_duration_seconds_bucket[5m]))",
"legendFormat": "50th percentile"
}
]
},
{
"title": "Database Queries",
"type": "graph",
"targets": [
{
"expr": "avg(laravel_app_database_queries_per_request)",
"legendFormat": "Avg queries per request"
}
]
},
{
"title": "Memory Usage",
"type": "graph",
"targets": [
{
"expr": "laravel_app_memory_usage_bytes",
"legendFormat": "Memory usage"
}
]
}
]
}
}
Production Deployment Checklist
✅ Security Configuration
- SSL certificates installed
- Security headers configured
- Firewall rules applied
- Database credentials secured
✅ Performance Optimization
- Opcache enabled
- Asset compression configured
- CDN integration completed
- Database indexes optimized
✅ Monitoring Setup
- Application monitoring configured
- Error tracking enabled
- Log aggregation implemented
- Alert notifications configured
✅ Backup Strategy
- Database backup automated
- File backup configured
- Backup restoration tested
- Offsite backup storage
✅ High Availability
- Load balancer configured
- Multiple application instances
- Database replication setup
- Queue workers running
📩 Need help with Laravel deployment?
Article Category
Deployment & DevOps: Laravel Production Deployment Mastery
Master Laravel deployment strategies, CI/CD pipelines, Docker containerization, server management, monitoring, and enterprise-level DevOps practices.
Consultation & Communication
Direct communication via WhatsApp or phone to understand your project needs precisely.
Planning & Scheduling
Creating clear work plan with specific timeline for each project phase.
Development & Coding
Building projects with latest technologies ensuring high performance and security.
Testing & Delivery
Comprehensive testing and thorough review before final project delivery.