Skip to content
Mehedi Hasan Sagor

// blog

Laravel Production Checklist: Best Practices Every Startup Should Enforce

Mehedi Hasan Sagor Oct 1, 2026 5 min read
Laravel Production Checklist: Best Practices Every Startup Should Enforce

1. Environment Configuration & Dependency Management

A production Laravel app should never rely on the default .env values that ship with the framework. Start by:

  • Never commit .env: Add it to .gitignore and store secrets in your CI/CD vault (e.g., GitHub Secrets, HashiCorp Vault, AWS Parameter Store).
  • Use environment‑specific config files: Create config/app.php entries that read from env() with sensible fallbacks, but avoid hard‑coding defaults that differ between local and production.
  • Lock dependencies: Run composer install --no-dev --optimize-autoloader on the build server. Commit the generated composer.lock to guarantee identical packages across deployments.
  • PHP version alignment: Declare the required PHP version in composer.json ("php": ">=8.2"). CI should fail if the build server runs an older interpreter.
# Example CI step
composer install --no-interaction --prefer-dist --no-dev --optimize-autoloader
php artisan config:cache
php artisan route:cache
php artisan view:cache

Caching the config, routes, and compiled views removes the overhead of parsing files on every request and is a prerequisite for high‑throughput workloads.

2. Database Strategy: Migrations, Seeds, and Read Replicas

Migrations

Treat migrations as code. Run them only in a controlled deployment window and never on the fly in production. Use php artisan migrate --force inside your CI pipeline after a successful build.

# Deploy step
php artisan migrate --force

Seeds & Data Integrity

Separate seed data that is required for the application to start (e.g., admin role, feature flags) from large reference tables. Keep these seeds idempotent so re‑running them does not create duplicates.

Read Replicas & Connection Pools

For read‑heavy workloads, configure a replica connection in config/database.php and route read queries using Laravel's read/write splitting:

'connections' => [
    'mysql' => [
        'read' => [
            'host' => env('DB_READ_HOST', 'replica.myapp.com'),
        ],
        'write' => [
            'host' => env('DB_WRITE_HOST', 'primary.myapp.com'),
        ],
        'driver' => 'mysql',
        // other options …
    ],
],

When you enable read/write splitting, make sure any transaction that writes also reads from the primary to avoid stale data.

3. Caching, Queues, and Asynchronous Work

Cache Layer

Laravel supports multiple cache drivers. In production, Redis is the default choice because it offers both key/value caching and pub/sub for queue back‑ends.

// config/cache.php
'default' => env('CACHE_DRIVER', 'redis'),

Cache expensive queries with remember():

$users = Cache::remember('active_users', now()->addMinutes(10), function () {
    return User::where('active', true)->get();
});

Queues

Never run time‑consuming jobs in the request cycle. Offload them to a queue driver such as Redis or Amazon SQS. Define a dedicated queue:work supervisor process (e.g., using Supervisor on Linux) that restarts automatically on failure.

[program:laravel-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work redis --sleep=3 --tries=3
numprocs=4
autostart=true
autorestart=true
user=www-data
stopwaitsecs=3600

Rate Limiting & Throttling

Laravel's built‑in rate limiter can protect APIs from abuse without adding external middleware.

Route::middleware('throttle:60,1')->group(function () {
    Route::get('/orders', [OrderController::class, 'index']);
});

4. Security Hardening

HTTPS Everywhere

Terminate TLS at the load balancer (AWS ALB, Cloudflare, etc.) and enforce APP_URL with https. In AppServiceProvider force HTTPS for all generated URLs:

public function boot()
{
    if (app()->environment('production')) {
        URL::forceScheme('https');
    }
}

HTTP Security Headers

Add a middleware that injects Content‑Security‑Policy, X‑Content‑Type‑Options, Referrer-Policy, and X‑Frame‑Options headers. Packages like spatie/laravel-csp simplify this.

// app/Http/Kernel.php
protected $middleware = [
    // …
    \Spatie\Csp\Middleware\AddCspHeaders::class,
];

Input Validation & Mass Assignment

Never rely on $request->all() for model creation. Use Form Request classes that define explicit validation rules and the $fillable array on Eloquent models.

class StorePostRequest extends FormRequest
{
    public function rules()
    {
        return [
            'title' => 'required|string|max:255',
            'body'  => 'required|string',
        ];
    }
}

// Controller
public function store(StorePostRequest $request)
{
    Post::create($request->validated());
}

Session & Cookie Settings

Set SESSION_SECURE_COOKIE=true, SESSION_HTTP_ONLY=true, and SESSION_SAME_SITE=lax in .env. For API‑only apps, consider stateless JWT tokens instead of server‑side sessions.

Regular Audits

Run php artisan security:check (provided by laravel/security-checker) in CI to detect known Composer vulnerabilities.

5. Monitoring, Logging, and Zero‑Downtime Deployments

Centralized Logging

Pipe Laravel logs to a structured logger like Monolog with a JSON formatter and ship them to a log aggregation service (Papertrail, Loggly, Elastic Stack).

// config/logging.php
'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['single', 'stderr'],
    ],
    'stderr' => [
        'driver' => 'monolog',
        'handler' => Monolog\Handler\StreamHandler::class,
        'formatter' => Monolog\Formatter\JsonFormatter::class,
        'with' => ['stream' => 'php://stderr'],
    ],
],

Application Performance Monitoring (APM)

Integrate an APM agent (e.g., Laravel Telescope for internal debugging, or third‑party services like New Relic, Datadog). Telescope should be enabled only in staging, never in production, to avoid overhead.

if (app()->environment('local', 'staging')) {
    $app->register(TelescopeServiceProvider::class);
}

Health Checks

Expose a /health endpoint that returns HTTP 200 only when critical services (DB, cache, queue) are reachable. Use Laravel's route closure:

Route::get('/health', function () {
    try {
        DB::connection()->getPdo();
        Cache::store()->ping();
        return response()->json(['status' => 'ok']);
    } catch (Throwable $e) {
        return response()->json(['status' => 'error'], 503);
    }
});

Load balancers can poll this endpoint for automatic instance replacement.

Zero‑Downtime Deploys

Leverage Blue‑Green or Canary deployments with tools like Laravel Envoy, GitHub Actions, or Terraform. The typical flow:

  1. Build artifact (Docker image or compiled assets).
  2. Deploy to a new target group.
  3. Run database migrations against the new version.
  4. Shift a small percentage of traffic.
  5. Monitor errors; if none, promote to 100 %.

If an issue appears, roll back by routing traffic back to the previous version and reverting the code base.


Takeaway

Production‑grade Laravel isn’t about a single setting; it’s a disciplined checklist that covers configuration, data handling, performance, security, and observability. By applying the practices above you can reduce emergency tickets, improve response times, and give your engineering team the confidence to iterate faster. Start by automating the checklist in your CI pipeline and treat each item as a gate that must pass before code reaches users.

  • Laravel
  • Security
  • Php
  • Devops
  • Performance