Laravel .env Explained: Secure Config Files for Production (2026)
Laravel .env Explained: Secure Config Files for Production (2026)
If you use Laravel 13 in real projects, your .env file becomes one of the most important files in the entire app. It controls application behavior, database access, mail credentials, queues, cache drivers, and more.
In this guide, you will learn how Laravel environment variables work, how to avoid common mistakes, and how to safely manage .env from local development to production.
What is .env in Laravel?
The .env file stores environment-specific values outside your codebase. Laravel reads these values at boot, then maps them into config files using env() calls.
Example:
APP_NAME="My Laravel App"
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost
This keeps secrets and environment differences out of source-controlled PHP files.
How Laravel uses environment values
Laravel does not expect you to call env() all over your app code. Best practice is:
- Read env vars in config files only (
config/*.php). - Access values in app code via
config('...').
Example in config/database.php:
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
],
Then in your app logic:
$driver = config('database.default');
This avoids surprises when you cache config in production.
The env() helper: when to use it and when not to
The env() helper is for configuration loading, not general runtime logic.
Use it here:
- Inside files in
config/*.php. - While defining default configuration values.
Avoid it here:
- Controllers, services, jobs, commands, and listeners.
- Blade templates and domain logic.
Good pattern:
// config/services.php
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],
// app/Services/MailerService.php
$token = config('services.postmark.token');
Why this matters: after php artisan config:cache, Laravel reads cached config values, so direct runtime env() calls can return unexpected values.
Environment detection in Laravel
Laravel primarily determines environment via APP_ENV, and you can inspect it using:
app()->environment();
app()->environment('local');
For CLI execution, Laravel also supports explicit environment overrides:
php artisan migrate --env=staging
php artisan migrate --env staging
Behavior to remember (matching framework detector tests):
--env=localuseslocal.--env localuseslocal.--envwith no value falls back to normal detection.- Similar flags like
--envelope=mailare ignored.
You can also provide custom detection logic in lower-level bootstrapping scenarios:
use Illuminate\Foundation\EnvironmentDetector;
$detector = new EnvironmentDetector;
$environment = $detector->detect(function () {
return 'production';
}, $_SERVER['argv'] ?? []);
In most applications, APP_ENV plus standard Laravel bootstrapping is enough.
Encrypting and decrypting environment files
For teams, Laravel supports encrypted environment files so secrets are not stored in plaintext when shared.
Encrypt your current .env file:
php artisan env:encrypt
Decrypt later:
php artisan env:decrypt
Useful notes:
- Encryption generates an encrypted env artifact (for example,
.env.encrypted). - Decryption requires the correct key, usually provided via CLI option or environment variable.
- Do not commit plaintext
.env; if you commit encrypted env files, protect keys in your secret manager.
This is especially useful when distributing secure defaults across multiple environments.
Writing variables to an environment file programmatically
In recent Laravel versions, you can write/update env variables using Env::writeVariables.
Example:
use Illuminate\Support\Env;
Env::writeVariables([
'APP_VIBE' => 'chill',
'DB_HOST' => '127:0:0:1',
'DB_PORT' => 3306,
'BRAND_NEW_PREFIX' => 'fresh value',
], base_path('.env'));
Overwrite existing keys explicitly:
Env::writeVariables([
'DB_CONNECTION' => 'sqlite',
'DB_HOST' => '127:0:0:1',
], base_path('.env'), true);
Practical guidance:
- Use this in setup/install flows, not normal request handling.
- Keep backups when mutating env files in automation.
- Validate generated env content in CI to avoid broken deploys.
Recommended .env baseline for Laravel 13 + MySQL 8
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database
# PHP_CLI_SERVER_WORKERS=4
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
# CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
For Docker/Sail setups, DB_HOST=mysql is correct because mysql is the container service name.
.env vs .env.example (and why both matter)
.envis your real, local or server-specific config file..env.exampleis the template committed to git for teammates and CI.
Every key in .env that others need should exist in .env.example with a safe placeholder.
Example:
APP_NAME="Laravel Starter"
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=laravel
DB_USERNAME=root
DB_PASSWORD=
Critical security rules for .env
Treat your .env like credentials, because it often contains credentials.
- Never commit
.envto git. - Never paste secrets in screenshots, PR descriptions, or logs.
- Rotate keys immediately if exposure happens.
- Use separate credentials per environment (local, staging, production).
- Keep production secrets in a secret manager or platform env settings.
Quick check:
git check-ignore -v .env
If .env is not ignored, fix .gitignore before doing anything else.
Why APP_DEBUG must be false in production
APP_DEBUG=true can leak sensitive internals (queries, stack traces, server paths).
Production minimum:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com
That one setting alone prevents a lot of accidental data exposure.
Config cache and env gotchas
When you run:
php artisan config:cache
Laravel compiles configuration into a cached file. If you update .env, changes may not be reflected until you clear/rebuild cache.
Useful commands:
php artisan config:clear
php artisan cache:clear
php artisan config:cache
If your app seems to ignore .env updates, cached config is usually the reason.
Local, staging, and production strategy
A practical approach for 2026:
- Local:
.envfile on your machine, Docker/Sail defaults. - Staging: environment vars set in hosting platform or deploy pipeline.
- Production: environment vars set in secure secret storage, never in repo.
This makes environment promotion predictable and auditable.
Common .env mistakes and fixes
| Mistake | Impact | Fix |
|---|---|---|
Calling env() directly in app code | Breaks when config is cached | Move to config/*.php, read via config() |
Committing .env | Secret exposure | Remove file from git, rotate credentials |
Missing key in .env.example | Onboarding failures | Add all required keys with placeholders |
APP_DEBUG=true in production | Sensitive data leakage | Set false in production and restart workers |
| Wrong DB host in Docker | Connection refused errors | Use service name (e.g., mysql) |
Quick sanity checklist before deploy
APP_ENV=productionandAPP_DEBUG=false.- Production DB credentials are not shared with staging/local.
.envis not tracked by git.- Config cache is rebuilt after env changes.
- Queue workers are restarted after deployment.
FAQ
Should .env ever be committed?
No. Commit .env.example only.
Is it okay to call env() in controllers or services?
Avoid it. Use config() in application code so behavior remains stable with cached config.
Where should production secrets live?
In your platform secret manager or runtime environment configuration, not in files inside the repository.
Do I need different .env values per environment?
Yes. Local, staging, and production should each use separate values and credentials.
Final takeaway
If you get .env management right early, you avoid painful deployment bugs and major security risks later. In Laravel 13, keep env values in config files, keep secrets out of git, and treat production settings as sensitive infrastructure data.
Next in this series: Create a New Laravel 13 Project in 2026, DB::raw guide, and the upcoming Laravel deployment guide.
