$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[October 27, 2024] Laravel

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)

────────────────────────────────────────────────────────
<-- back to posts