Laravel Deployment Best Practices
Laravel Deployment: Tips & Tricks
Laravel Deployment: Tips & Tricks
Tip: The Deployment Checklist
composer install --no-dev --optimize-autoloader
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan migrate --force
php artisan storage:link
npm run build
Run in this order. Config cache must come before route cache.
Gotcha: Don't Cache Config Before Migrations
If your migrations read config values, cache them first. But if migrations add config, cache after.
Tip: Use Zero-Downtime Deployment
Tools like Laravel Envoyer or Deployer deploy to a new directory, then symlink. No downtime.
Gotcha: Queue Workers Need Restart After Deploy
Workers load code into memory. After deployment, restart them:
php artisan queue:restart
This signals workers to restart after finishing their current job.
Tip: Health Check Endpoint
Route::get('/health', function () {
return response()->json([
'status' => 'ok',
'time' => now()->toIso8601String(),
]);
});
For load balancers and monitoring.
Gotcha: APP_DEBUG=true in Production
This exposes stack traces, config values, and environment details. Always false in production.
Tip: Use Laravel Nightwatch for Monitoring
composer require laravel/nightwatch
Built-in monitoring for Laravel apps.
Tip: Pre-warm the Cache After Deploy
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
Clear old caches before building new ones.
Tip: Use route:cache Carefully
php artisan route:cache is fast, but it doesn't work with closure-based routes. Every time you cache routes, Laravel serializes them. If you have Route::redirect() or closure callbacks, the cache breaks. Stick to controller-based routes in production.
Tip: Model APP_KEY Rotation
Rotating APP_KEY invalidates all encrypted data — cookies, encrypted DB columns, and password reset tokens. If you must rotate (e.g., after a leak), plan a migration that re-encrypts existing data with the new key.
Gotcha: Local Scope Leaks
Global scopes defined in booted() apply to ALL queries on that model — including relationships. An innocent User::all() in admin panel might exclude soft-deleted users if a global scope is active.
Senior Insight
Deployment is where the 'it works on my machine' problem becomes a production incident. After years of managing Laravel deployments, the single most impactful change I made was adding a pre-deploy checklist that runs config:cache, route:cache, view:cache, and event:cache in sequence — and validates each step succeeded before proceeding. Without cached config, a single missing .env value can bring down your entire application silently.
Source: Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/laravel), Spatie Blog (https://spatie.be/blog)