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:

  1. Read env vars in config files only (config/*.php).
  2. 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=local uses local.
  • --env local uses local.
  • --env with no value falls back to normal detection.
  • Similar flags like --envelope=mail are 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)

  • .env is your real, local or server-specific config file.
  • .env.example is 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 .env to 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: .env file 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

MistakeImpactFix
Calling env() directly in app codeBreaks when config is cachedMove to config/*.php, read via config()
Committing .envSecret exposureRemove file from git, rotate credentials
Missing key in .env.exampleOnboarding failuresAdd all required keys with placeholders
APP_DEBUG=true in productionSensitive data leakageSet false in production and restart workers
Wrong DB host in DockerConnection refused errorsUse service name (e.g., mysql)

Quick sanity checklist before deploy

  1. APP_ENV=production and APP_DEBUG=false.
  2. Production DB credentials are not shared with staging/local.
  3. .env is not tracked by git.
  4. Config cache is rebuilt after env changes.
  5. 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.