Skip to content
Mehedi Hasan Sagor

// blog

A Production‑Ready Laravel Checklist: From Code to Cloud

Mehedi Hasan Sagor Oct 19, 2026 6 min read
A Production‑Ready Laravel Checklist: From Code to Cloud

1. Start with a Clean, Version‑Controlled Codebase

Before you think about servers, make sure the repository itself follows solid conventions.

  • Git flow – Use a protected main (or master) branch and feature branches that open a pull request before merging. Enforce code review and require that CI passes.
  • Composer lock – Commit composer.lock so every environment installs the exact same dependency versions.
  • Environment variables – Store secrets in .env files that are never committed. Use a tool like dotenv or a cloud secret manager to inject them at deploy time.
  • Static analysis – Run phpstan (level 7+) and laravel‑pint on every PR. They catch type errors and style violations early, reducing runtime bugs.
# Example CI step (GitHub Actions)
composer install --no-dev --optimize-autoloader
vendor/bin/phpstan analyse --level=7
vendor/bin/pint --test

2. Configuration for Performance and Stability

Laravel ships with sensible defaults, but production needs a few tweaks.

| Setting | Why it matters | Recommended value | |---------|----------------|-------------------| | APP_DEBUG | Prevents stack traces from leaking to users. | false | | APP_ENV | Enables environment‑specific service providers. | production | | CACHE_DRIVER | Determines where cached data lives. | redis | | SESSION_DRIVER | Session storage must survive multiple web servers. | redis | | QUEUE_CONNECTION | Background jobs should be processed by a dedicated worker pool. | redis | | LOG_CHANNEL | Centralised logs simplify troubleshooting. | stack (with daily files + syslog) |

Opcode caching – Enable PHP‑OPcache in php.ini and set opcache.memory_consumption=256 (or higher for large apps). This alone can shave 30‑40 % off request time.

3. Caching Strategy – What to Cache and When

Caching is the single most effective lever for scaling Laravel.

  1. Configuration & routes – Run php artisan config:cache and php artisan route:cache during deployment. These generate serialized files that the framework loads in a single require.
  2. Views – Pre‑compile Blade templates with php artisan view:cache. This avoids on‑the‑fly compilation for every request.
  3. Query results – Use the Cache façade for data that changes infrequently.
// Example: caching a heavy query for 10 minutes
use IlluminateSupportFacadesCache;

$users = Cache::remember('active-users', now()->addMinutes(10), function () {
    return User::where('status', 'active')->with('profile')->get();
});
  1. Response caching – For public endpoints, wrap the controller with Cache::rememberForever or use middleware like spatie/laravel-responsecache.
  2. Tagging – When you need to invalidate a group of keys (e.g., after a product price update), store them under a tag:
Cache::tags(['products'])->flush(); // clears all product‑related caches

Tip: Avoid caching whole Eloquent collections when they contain sensitive data. Instead, cache only the fields you need.

4. Queue Workers – Keep the Main Thread Light

Long‑running tasks (emails, video processing, webhooks) belong in queues.

  • Supervisor – Use supervisord to keep a pool of php artisan queue:work --daemon processes alive. A typical config:
[program:laravel-queue]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/artisan queue:work redis --sleep=3 --tries=3
autostart=true
autorestart=true
numprocs=4
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/laravel/queue.log
  • Back‑off & retry – Laravel 10 introduced exponential back‑off via retryAfter. Set a sensible delay to avoid hammering external APIs.
  • Failed jobs – Store failures in a dedicated table (php artisan queue:failed-table). Monitor it daily; a spike often signals downstream outages.

5. Database Health – Indexes, Read Replicas, and Zero‑Downtime Migrations

Even though the post about zero‑downtime migrations exists elsewhere, a quick reminder on the surrounding practices is still valuable.

  • Index audit – Run php artisan db:explain on slow queries and add missing indexes. Use tools like Laravel Telescope or MySQL's slow_query_log to surface candidates.
  • Read replicas – Offload analytics and reporting to a read‑only replica. Configure a separate connection in config/database.php and use the read/write split.
  • Connection pooling – When using Laravel Octane with Swoole or RoadRunner, enable persistent connections to reduce handshake overhead.

6. Security Essentials for Production

Security is non‑negotiable. Follow these baseline steps:

  1. HTTPS everywhere – Enforce APP_URL with https:// and enable HSTS via web server headers.
  2. Content Security Policy – Prevent XSS by defining a strict CSP header. Packages like spatie/laravel-csp simplify this.
  3. Rate limiting – Use Laravel's built‑in RateLimiter to protect login, password reset, and API endpoints.
  4. Password hashing – Laravel uses bcrypt by default; consider argon2id for stronger protection ('driver' => 'argon' in config/hashing.php).
  5. Audit logs – Record critical actions (admin changes, payment status updates) in an immutable log table. Store logs in a separate write‑once bucket (e.g., AWS S3 with Object Lock) for compliance.
// Example: rate limiting login attempts
use IlluminateCacheRateLimitingLimit;
use IlluminateSupportFacadesRateLimiter;

RateLimiter::for('login', function (Request $request) {
    return Limit::perMinute(5)->by($request->ip());
});

7. Observability – Know When Things Go Wrong

A production‑ready app must be observable without guessing.

  • Logging – Use Monolog’s stack channel to write daily files and forward JSON logs to a log aggregation service (e.g., Loggly, Papertrail). Include request IDs (X-Request-ID) for traceability.
  • Metrics – Export key counters (queue lag, cache hit ratio, DB connections) via Prometheus. The spatie/laravel-metrics package provides a quick start.
  • Health checks – Implement an endpoint (/health) that returns 200 only when DB, cache, and queue connections are healthy. Use a simple controller:
public function __invoke()
{
    return response()->json([
        'database' => DB::connection()->getPdo() ? 'ok' : 'fail',
        'cache'    => Cache::store()->ping() ? 'ok' : 'fail',
        'queue'    => Queue::connection()->size() >= 0 ? 'ok' : 'fail',
    ]);
}

Configure your load balancer or uptime monitor to poll this endpoint.

8. Continuous Integration & Deployment (CI/CD)

Automation reduces human error and speeds up releases.

  • Build stage – Run composer install --no-dev --prefer-dist --optimize-autoloader, npm ci && npm run prod, and the test suite (php artisan test). Fail fast.
  • Artifact storage – Store the built assets (JS, CSS) in a versioned bucket (e.g., S3) and point the CDN to it. This eliminates the need for the server to compile assets on the fly.
  • Zero‑downtime deploy – Deploy to a new release directory, run artisan migrate --force, then atomically switch the web root symlink. Tools like Laravel Envoy or Deployer make this straightforward.
# Simple Deployer recipe snippet
task('deploy:release', function () {
    run('mkdir -p {{release_path}}');
    run('git clone {{repository}} {{release_path}}');
    run('cd {{release_path}} && composer install --no-dev --optimize-autoloader');
    run('cd {{release_path}} && php artisan migrate --force');
    run('ln -nfs {{release_path}} {{current_path}}');
});
  • Rollback plan – Keep the previous release directory for at least one hour. If the new version fails health checks, revert the symlink.

9. Documentation & Knowledge Transfer

Even the best‑engineered system can become a liability without clear docs.

  • API contracts – Keep an OpenAPI spec (swagger.yaml) in the repo. Generate client SDKs automatically.
  • Runbooks – Write concise steps for common incidents (cache flush, queue worker restart, DB replica lag). Store them in a searchable wiki.
  • On‑call rotation – Use tools like PagerDuty to rotate alerts. Ensure every on‑call engineer has access to the same runbooks.

10. Takeaway: Turn the Checklist into a Habit

A Laravel app doesn’t become production‑ready by a single configuration change; it’s the result of disciplined habits:

  1. Automate every repeatable step – from linting to deployment.
  2. Measure performance and error rates daily; let alerts drive action.
  3. Iterate – Treat the checklist as a living document. As your traffic grows, revisit cache TTLs, queue worker counts, and DB indexing.

By embedding these practices into your development workflow, you give founders and technical leads confidence that the product can scale, stay secure, and keep delivering value without firefighting.

  • Laravel
  • Security
  • Devops
  • Performance