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(ormaster) branch and feature branches that open a pull request before merging. Enforce code review and require that CI passes. - Composer lock – Commit
composer.lockso every environment installs the exact same dependency versions. - Environment variables – Store secrets in
.envfiles that are never committed. Use a tool likedotenvor a cloud secret manager to inject them at deploy time. - Static analysis – Run
phpstan(level 7+) andlaravel‑pinton 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.
- Configuration & routes – Run
php artisan config:cacheandphp artisan route:cacheduring deployment. These generate serialized files that the framework loads in a singlerequire. - Views – Pre‑compile Blade templates with
php artisan view:cache. This avoids on‑the‑fly compilation for every request. - Query results – Use the
Cachefaç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();
});
- Response caching – For public endpoints, wrap the controller with
Cache::rememberForeveror use middleware likespatie/laravel-responsecache. - 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
supervisordto keep a pool ofphp artisan queue:work --daemonprocesses 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:explainon slow queries and add missing indexes. Use tools like Laravel Telescope or MySQL'sslow_query_logto surface candidates. - Read replicas – Offload analytics and reporting to a read‑only replica. Configure a separate connection in
config/database.phpand use theread/writesplit. - 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:
- HTTPS everywhere – Enforce
APP_URLwithhttps://and enable HSTS via web server headers. - Content Security Policy – Prevent XSS by defining a strict CSP header. Packages like
spatie/laravel-cspsimplify this. - Rate limiting – Use Laravel's built‑in
RateLimiterto protect login, password reset, and API endpoints. - Password hashing – Laravel uses
bcryptby default; considerargon2idfor stronger protection ('driver' => 'argon'inconfig/hashing.php). - 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
stackchannel 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-metricspackage 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:
- Automate every repeatable step – from linting to deployment.
- Measure performance and error rates daily; let alerts drive action.
- 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