The env() trap that silently kills your performance.
This is the #1 mistake that takes down production Laravel apps. Developers use env() in their code, run php artisan config:cache, and suddenly their app returns null for every config value. The app dies. They spend hours debugging.
On every single HTTP request, Laravel:
.env file from diskKEY=VALUE pairsconfig/*.php files (20-50 files typically)// config/database.php (typical — WRONG for production)
return [
'connections' => [
'mysql' => [
'host' => env('DB_HOST', '127.0.0.1'), // ← env() call
'port' => env('DB_PORT', '3306'), // ← env() call
'database' => env('DB_DATABASE', 'forge'), // ← env() call
'username' => env('DB_USERNAME', 'forge'), // ← env() call
'password' => env('DB_PASSWORD', ''), // ← env() call
],
],
];
// In controllers or models (EVEN WORSE):
$host = env('DB_HOST'); // ← Reads .env file directly!
$apiKey = env('STRIPE_KEY'); // ← Another file read!
Every env() call = file I/O operation = reading .env from disk. If you have 50 config values, that's 50 file reads per request. If you call env() inside a loop, you're dead.
Step 1: Developer writes code like this:
// app/Http/Controllers/UserController.php
$apiKey = env('STRIPE_SECRET_KEY'); // ← PROBLEM
Step 2: Developer runs php artisan config:cache
Step 3: Laravel generates cached config... but env() outside config files is NOT evaluated
Step 4: env('STRIPE_SECRET_KEY') returns null
Step 5: Payment processing fails. App crashes. Developer confused.
But this command is dangerous if you break the golden rule.
// config/app.php
'name' => env('APP_NAME'), // ← OK here
// app/Http/Controllers/HomeController.php
$appName = env('APP_NAME'); // ← DEATH. Will return null after cache.
// database/migrations/2024_01_01_create_users_table.php
DB::statement('CREATE DATABASE ' . env('DB_DATABASE')); // ← DEATH
// config/app.php
'name' => env('APP_NAME'), // ← OK here
// app/Http/Controllers/HomeController.php
$appName = config('app.name'); // ← OK. Uses cached config.
// database/migrations/2024_01_01_create_users_table.php
$database = config('database.connections.mysql.database'); // ← OK
| .env file reads per request | 1 (opens entire file) |
| Config files parsed per request | 20-50 |
| env() calls executed | 30-100 |
| Time per request (config only) | 15-40ms |
| .env file reads per request | 0 (cached) |
| Config files parsed per request | 1 (cached array) |
| env() calls executed | 0 |
| Time per request (config only) | 1-3ms |
| Metric | Without Cache | With Cache | Improvement |
|---|---|---|---|
| Config file I/O per request | ~50 files | 1 file | 98% ↓ |
| Time spent on config (per request) | 25ms | 2ms | 92% ↓ |
| 10,000 requests per day | 250 seconds (4.1 minutes CPU) | 20 seconds | 230 seconds saved daily |
This runs:
php artisan config:cache — caches configuration (THIS TOPIC)php artisan route:cache — caches routes (Topic 2)php artisan event:cache — caches event listenersphp artisan view:cache — precompiles Blade templatesIf you do any of these, config:cache will break:
env() in controllers, models, migrations, or commandsenv() in Blade templates ({{ env('APP_NAME') }})env() in service providers outside the register() methodRun this command in your project root to find all env() calls outside config/:
grep -r "env(" --exclude-dir=vendor --exclude-dir=storage --exclude-dir=bootstrap/cache --include="*.php" | grep -v "config/"
If you see any results, fix them before running config:cache in production.
| Location | Use env()? | Use config()? |
|---|---|---|
config/*.php |
✅ YES (the only place) | ❌ No (not yet built) |
app/Http/Controllers/*.php |
❌ NO | ✅ YES |
app/Models/*.php |
❌ NO | ✅ YES |
database/migrations/*.php |
❌ NO | ✅ YES |
resources/views/*.blade.php |
❌ NO | ✅ YES (config('app.name')) |
app/Console/Commands/*.php |
❌ NO | ✅ YES |
| Concept | Without Cache | With Cache |
|---|---|---|
| .env file reads | Every request (opens entire file) | Never (cached) |
| Config files parsed | 20-50 files per request | 1 cached file |
| env() usage allowed where? | Anywhere (but slow) | Only in config/*.php |
| Command | — | php artisan config:cache or php artisan optimize |
config() everywhere else. Run php artisan optimize after every deployment.Topic 4: Eloquent N+1 Killer — The #1 performance disaster in Laravel. Why 101 queries run when 2 would suffice. And how preventLazyLoading() saves your soul.