0

.env and the vlucas/phpdotenv library Laravel uses

Beginner5 min read·lv-05-001

Concept

Laravel uses vlucas/phpdotenv to load environment variables from a .env file into PHP's $_ENV and $_SERVER superglobals, and into getenv(). This allows configuration to differ between environments (local, staging, production) without changing code.

How it works: At boot time (public/index.php), Dotenv\Dotenv::createImmutable(base_path())->load() reads .env from the project root and calls putenv() for each variable. After loading, env('KEY') reads from $_ENV / getenv().

.env rules:

  • KEY=value — simple string.
  • KEY= — empty string.
  • KEY="value with spaces" — quote values with spaces.
  • KEY='literal $not interpolated' — single quotes prevent expansion.
  • # comment — comment lines.
  • KEY="${ANOTHER_KEY}/suffix" — variable expansion (phpdotenv 5+).
  • Never quote boolean values: APP_DEBUG=true, not APP_DEBUG="true" (phpdotenv converts to real booleans for true, false, null, 1, 0).

.env.example: A committed template with all keys but no sensitive values. Developers copy it to .env and fill in their values. Never commit .env itself — it contains secrets.

Multiple env files: phpdotenv can load multiple files in order. Laravel also supports .env.testing (loaded when APP_ENV=testing) and .env.{environment} overrides.

Security: Never commit .env. Add .env to .gitignore. Use environment variables in production (from the server, Docker secrets, or a secrets manager) rather than .env files.

Code Example

bash
# .env
APP_NAME="The Framework Architect"
APP_ENV=local
APP_KEY=base64:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
APP_DEBUG=true
APP_URL=http://localhost:8000

DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite

CACHE_STORE=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379

MAIL_MAILER=log
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

# .env.example — committed, no real values
APP_NAME=Laravel
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=
php
<?php
// Reading env values
$appName = env('APP_NAME');             // "The Framework Architect"
$debug   = env('APP_DEBUG');            // true (boolean — not string "true"!)
$dbHost  = env('DB_HOST', '127.0.0.1'); // with default if not set

// IMPORTANT: env() should ONLY be called in config files
// Not in controllers, services, or anywhere else!
// Use config() everywhere else:
// config('app.name') reads the value cached from config/app.php
// which reads env('APP_NAME') exactly once at boot

// Why? php artisan config:cache writes all config to a file.
// After caching, .env is NOT read. env() calls outside config files return null!

// WRONG — calling env() outside a config file:
class UserService
{
    public function __construct()
    {
        $this->apiKey = env('API_KEY'); // returns null after config:cache!
    }
}

// CORRECT — read from config:
// config/services.php: 'api_key' => env('API_KEY'),
class UserService
{
    public function __construct()
    {
        $this->apiKey = config('services.api_key'); // works always
    }
}