Introduction
Laravel keeps secrets and per-environment values in a .env file at the project root. This lesson covers the rules for reading them, why env() belongs inside config files, and how to layer multiple environment files.
Key Concepts
.env: A plain key/value file with one variable per line, never committed to git.env()helper: Reads a variable from the parsed.envfile..env.example: A template checked into git with safe defaults.config:cache: Freezes configuration —env()calls outside config files returnnullafterwards.- Environment detection:
App::environment('production')for conditional logic.
Real World Context
Stripe keys, database passwords, and API tokens need to differ between local, staging, and production. Storing them in .env keeps the same code shipping to every environment without secrets leaking into version control.
Deep Dive
Laravel uses environment variables to manage settings that differ between development, staging, and production. This keeps sensitive information out of your codebase.
Why Environment Variables?
┌─────────────────────────────────────────────────────────────┐
│ Your Codebase │
│ (version control) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ config/database.php │ │
│ │ 'host' => env('DB_HOST', 'localhost') │ │
│ │ 'database' => env('DB_DATABASE', 'forge') │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
Uses values from environment
│
┌─────────────────┴─────────────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ .env (local) │ │.env (prod) │
│ DB_HOST= │ │ DB_HOST= │
│ localhost │ │ prod-db.aws │
│ DB_DATABASE= │ │ DB_DATABASE=│
│ my_app_dev │ │ my_app_prod │
└──────────────┘ └──────────────┘
The .env File
The .env file contains your environment-specific settings:
env# Application APP_NAME="My Application" APP_ENV=local APP_KEY=base64:random_key_here APP_DEBUG=true APP_URL=http://localhost # Database DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=my_app DB_USERNAME=root DB_PASSWORD=secret # Mail MAIL_MAILER=smtp MAIL_HOST=mailhog MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_FROM_ADDRESS="hello@example.com" MAIL_FROM_NAME="${APP_NAME}" # Can reference other variables # Third-party Services STRIPE_KEY=sk_test_... STRIPE_SECRET=pk_test_...
The env() Helper
Access environment variables with the env() function:
php// Get environment variable $debug = env('APP_DEBUG'); // With a default value $host = env('DB_HOST', 'localhost'); // In configuration files // config/database.php return [ 'default' => env('DB_CONNECTION', 'sqlite'), 'connections' => [ 'mysql' => [ 'host' => env('DB_HOST', '127.0.0.1'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), ], ], ];
Important: Only Use env() in Config Files
Never use env() directly in your application code:
php// ❌ Bad - Don't do this in controllers, models, etc. public function index() { $apiKey = env('API_KEY'); // May return null in production! } // ✅ Good - Use config() instead public function index() { $apiKey = config('services.api.key'); } // And define it in config/services.php return [ 'api' => [ 'key' => env('API_KEY'), ], ];
Why? Because in production, configuration is cached and env() calls outside config files return null.
Environment Detection
Check the current environment:
php// Using App facade use Illuminate\Support\Facades\App; if (App::environment('local')) { // Running locally } if (App::environment(['local', 'staging'])) { // Local or staging } // Using the app() helper if (app()->environment('production')) { // Production environment } // Using config if (config('app.env') === 'local') { // Local environment }
The .env.example File
The .env.example file is a template that should be in version control:
env# .env.example - Committed to version control APP_NAME=Laravel APP_ENV=local APP_KEY= APP_DEBUG=true DB_CONNECTION=sqlite # DB_HOST=127.0.0.1 # DB_PORT=3306 # DB_DATABASE=laravel # DB_USERNAME=root # DB_PASSWORD=
When someone clones your project, they copy .env.example to .env and fill in their values.
Configuration Caching
In production, cache configuration for performance:
bash# Cache all configuration into a single file php artisan config:cache # Clear the cache php artisan config:clear
Important: After caching, env() calls only work inside config files!
Different Environments
You can have multiple environment files:
.env # Default (development)
.env.local # Local overrides
.env.staging # Staging environment
.env.production # Production (but usually set via server)
.env.testing # Testing environment
Set which file to use:
bash# Use a specific environment file APP_ENV=staging php artisan migrate
Common Pitfalls
- Reading secrets with
env()in controllers — Works locally, returnsnullonce production runsconfig:cache. Always go throughconfig(). - Referencing unset variables —
env('SOMETHING')silently returnsnull. Fail fast by requiring defaults in your config files.
Best Practices
- Keep
.env.exampleup to date — New collaborators copy it to.envand expect it to work. - Use the
${VAR}syntax —MAIL_FROM_NAME="${APP_NAME}"reuses already-declared variables for consistency.
Summary
.envstores secrets and per-environment config and is git-ignored.- Only call
env()insideconfig/*.phpfiles; useconfig()everywhere else. .env.exampleis the committed template.App::environment()returns the current environment string.config:cachebreaksenv()calls outside config files — design around it.