Appearance
Meeting 13 - Laravel Environment & Configuration
Goal: Understand how Laravel handles environments (local, staging, production, testing), how
.envis loaded, how configuration files work, secrets management, performance optimizations, and safe deployment practices.
1. Core Concepts
| Concept | Description |
|---|---|
| Environment | Logical context: local, staging, production, testing, etc. Affects debugging, logging, caching, and 3rd party integrations. |
.env File | Key/value pairs loaded early (via vlucas/phpdotenv) into $_ENV & env() during bootstrap. Never commit real secrets. |
| Config Files | PHP arrays in config/*.php centralize settings. Access with config('app.timezone'). |
| Service Container | Resolves dependencies; some bindings differ per environment. |
| Caching | Config, routes, events, views can be cached to speed bootstrap. |
| APP_KEY | Cryptographic key used for encryption & hashing (cookies, password reset tokens). Must be set & kept secret. |
2. Environment Variables (.env)
Example minimal .env:
dotenv
APP_NAME="MyApp"
APP_ENV=local
APP_KEY=base64:GENERATED_KEY
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=secret
CACHE_DRIVER=file
QUEUE_CONNECTION=database
SESSION_DRIVER=fileAPP_NAME="MyApp"
APP_ENV=local
APP_KEY=base64:GENERATED_KEY
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=secret
CACHE_DRIVER=file
QUEUE_CONNECTION=database
SESSION_DRIVER=fileNotes:
APP_DEBUGshould be false in production.- Separate
.env.testingcan override values during automated tests. - Use different DBs per environment to avoid data loss.
3. How Laravel Loads Configuration
public/index.php-> bootstrap/app..envis parsed early; values are accessible withenv()only during config load.- Each
config/*.phpfile returns an array; many items callenv('KEY'). - After loading, you should use
config()helper (notenv()) inside your application logic (controllers, services, jobs). Why? Because when config is cached,env()calls return null.
Example (config/app.php excerpt):
php
'timezone' => env('APP_TIMEZONE', 'UTC'),
'name' => env('APP_NAME', 'Laravel'),'timezone' => env('APP_TIMEZONE', 'UTC'),
'name' => env('APP_NAME', 'Laravel'),Usage in code:
php
config('app.timezone'); // good
// env('APP_TIMEZONE'); // avoid (except inside config/*.php)config('app.timezone'); // good
// env('APP_TIMEZONE'); // avoid (except inside config/*.php)4. Config Caching & Optimization
Boost performance in production by reducing file I/O.
| Command | Purpose | When to Use |
|---|---|---|
php artisan config:cache | Merge all config into one file | On deploy (after composer install) |
php artisan route:cache | Cache route definitions | For stable route sets (mostly production) |
php artisan view:cache | Precompile Blade templates | Optional for prod |
php artisan event:cache | Cache event/listener mappings | If using events heavily |
php artisan optimize | Runs several optimizers (Laravel 10+) | Convenience wrapper |
php artisan clear-compiled | Clear compiled files (older versions) | Rarely needed |
Clear caches when needed:
bash
php artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clearphp artisan config:clear
php artisan route:clear
php artisan view:clear
php artisan cache:clear5. Environment Separation Strategy
| Aspect | Local | Staging | Production |
|---|---|---|---|
| APP_ENV | local | staging | production |
| APP_DEBUG | true | false | false |
| Logging | verbose | info/warning | warning/error |
| Cache Driver | file | redis | redis/memcached |
| Queue Driver | database | redis | redis |
| DB | local dev db | staging db | production db |
| 3rd Party Keys | test keys | test keys | live keys |
6. Secrets Management
Best practices:
- NEVER commit
.envto version control (only.env.example). - Use infrastructure secret stores for production (e.g., Docker secrets, Kubernetes secrets, AWS SSM, Vault). Inject into environment before PHP-FPM starts.
- Rotate keys (DB, API, OAuth) regularly.
- Restrict
APP_KEYaccess: treat like a password—it secures encrypted cookies.
Example .env.example snippet (no real secrets):
dotenv
APP_NAME="MyApp"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=APP_NAME="MyApp"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=7. Service Container & Environment
You can bind different implementations by environment:
php
// In a service provider
public function register(): void
{
if (app()->environment('local')) {
$this->app->bind(App\Contracts\PaymentGateway::class, App\Services\FakeGateway::class);
} else {
$this->app->bind(App\Contracts\PaymentGateway::class, App\Services\StripeGateway::class);
}
}// In a service provider
public function register(): void
{
if (app()->environment('local')) {
$this->app->bind(App\Contracts\PaymentGateway::class, App\Services\FakeGateway::class);
} else {
$this->app->bind(App\Contracts\PaymentGateway::class, App\Services\StripeGateway::class);
}
}Multiple environment detection helpers:
php
app()->environment(); // current name
app()->environment('local'); // boolean
app()->environment(['staging','production']); // booleanapp()->environment(); // current name
app()->environment('local'); // boolean
app()->environment(['staging','production']); // boolean8. Configuration Patterns
8.1 Centralized Configuration Class (Optional Wrapper)
php
class FeatureFlags
{
public static function isBetaEnabled(): bool
{
return (bool) config('features.beta');
}
}class FeatureFlags
{
public static function isBetaEnabled(): bool
{
return (bool) config('features.beta');
}
}config/features.php:
php
return [
'beta' => env('FEATURE_BETA', false),
];return [
'beta' => env('FEATURE_BETA', false),
];8.2 Per-Environment File Override (Advanced)
You can load extra config per environment by checking app()->environment() within a service provider and merging arrays (config([...])). Keep this minimal to avoid confusion.
9. Testing Environment
- PHPUnit automatically loads
.env.testingif present. - Use dedicated database: set
DB_DATABASE=myapp_test. - Run migrations fresh for isolation:
bash
php artisan test --env=testing
# or
php artisan test --parallelphp artisan test --env=testing
# or
php artisan test --parallel- Use
RefreshDatabasetrait in tests to migrate before each test suite.
10. Deployment Checklist (Environment Focused)
- Copy/Inject
.env(or environment variables) – ensureAPP_KEYset. - Run
composer install --no-dev --optimize-autoloader. - Run database migrations:
php artisan migrate --force. - Cache config & routes:
php artisan config:cache && php artisan route:cache. - (Optional) Cache views & events:
php artisan view:cache && php artisan event:cache. - Queue workers / horizon restarted:
php artisan queue:restart. - Ensure storage & bootstrap/cache writable.
- Monitor logs & health endpoint after deploy.
11. Common Pitfalls
| Issue | Cause | Fix |
|---|---|---|
env() returns null in app code | Config cached; you used env() outside config files | Use config() helper |
Stale config after editing .env | Config cache not cleared | Run php artisan config:clear |
APP_KEY missing error | Key not generated | php artisan key:generate |
| Debug mode exposed | APP_DEBUG=true in production | Set false & clear config |
| Mixed staging & production keys | Reused .env accidentally | Maintain separate secrets store |
12. Summary
Laravel environment management = .env + config/*.php + caching + container decisions. Treat configuration as code (but secrets as data). Cache for speed, isolate per environment, and never leak secrets through debug pages.