env
Reads an environment variable loaded from .env, returning a default when the key is not set.
env(string $key, mixed $default = null): mixedParameters
| Parameter | Type | Description |
|---|---|---|
$$key | string | Name of the environment variable to read, exactly as it is written in the .env file. |
$$defaultoptional | mixed= null | Returned when no environment variable exists for the key. A closure is resolved, so an expensive fallback is only computed when it is needed. |
Return value
mixedThe environment variable's value, with the reserved words true, false, null and empty converted to their PHP types; otherwise the default.
Errors
Nothing on its own — an unknown key quietly returns the default; use Illuminate\Support\Env::getOrFail() when a missing variable should raise a RuntimeException instead.
env reads a value that DotEnv loaded from your .env file into the environment repository, falling back to the second argument when the key is not set. It is the bridge between a deployment's secrets and your configuration files, and that is the only place it belongs.
When to reach for it
- Inside
config/*.php, to feed a configuration key from a deployment-specific value. - In a package's published config file, so installers can override defaults without editing PHP.
- In
bootstrap/code that runs before configuration is available.
What trips people up
Values arrive as strings, with a short list of reserved words converted for you: true and (true) become true, false and (false) become false, null and (null) become null, and empty and (empty) become an empty string. Everything else — ports, timeouts, sizes — stays a string, so cast at the config file: (int) env('REDIS_PORT', 6379). A value containing spaces must be wrapped in double quotes.
The second trap is caching. After php artisan config:cache the .env file is never loaded again, so a call from a controller, model or service returns only genuine server-level variables, and null for everything else. Read config('services.stripe.key') in application code instead.
Example
<?php
// .env
// APP_NAME="My Application"
// APP_DEBUG=true
// BILLING_CURRENCY=(empty)
// MAIL_REPLY_TO=null
var_dump(env('APP_NAME'));
var_dump(env('APP_DEBUG'));
var_dump(env('BILLING_CURRENCY'));
var_dump(env('MAIL_REPLY_TO'));
var_dump(env('STRIPE_SECRET', 'sk_test'));
// string(14) "My Application"
// bool(true)
// string(0) ""
// NULL
// string(7) "sk_test"When to use it
- Point a config key at a per-environment credential, such as env('STRIPE_SECRET') in config/services.php.
- Give a package a sensible default that operators can override from .env without touching PHP.
- Flip a feature on per environment with (bool) env('FEATURE_BILLING', false).
- Cast a numeric setting once, in config, so the rest of the app receives an int rather than a string.
Frequently asked questions
Why does the value come back null in production but work locally?
Because php artisan config:cache was run during deployment, and once the configuration is cached the .env file is no longer loaded. Only real server-level or system-level environment variables are visible; everything else resolves to null. Move the call into a config file and read config('...') from your application code.
Can I call it outside of a config file?
You can, but you should not, in any application that caches its configuration. Calls from controllers, models, jobs, service providers and Blade templates all break the moment config:cache runs. Config files, bootstrap code, and code that runs before the framework boots are the safe places.
How do I get a boolean or an integer out of a .env file?
The literals true, (true), false, (false), null, (null), empty and (empty) are converted to PHP types automatically; every other value is a string. For numbers and anything else, cast where you read it: (int) env('REDIS_PORT', 6379) or (bool) env('FEATURE_FLAG', false).
More examples
The only place it belongs: a config file
The .env value is read once, while the config file is being built, and cast there. Application code then asks for config('billing.retries') and always receives an int — including after php artisan config:cache, when the .env file is no longer loaded.
<?php
// config/billing.php
return [
'secret' => env('STRIPE_SECRET'),
'currency' => env('BILLING_CURRENCY', 'usd'),
'retries' => (int) env('BILLING_RETRIES', 3),
'enabled' => (bool) env('BILLING_ENABLED', false),
];
// Anywhere else in the application, read the cached config instead:
echo config('billing.currency'), PHP_EOL;
var_dump(config('billing.retries'));
// usd
// int(3)Why the same call breaks in production
Both lines look identical in local development because the .env file is still being parsed on every request. On a deployed server with a cached configuration only the config() call keeps working, which is the single most common source of "it works on my machine" credential bugs.
<?php
// app/Services/StripeClient.php
class StripeClient
{
public function __construct()
{
// Works locally. After `php artisan config:cache` on the server,
// .env is never loaded, so this is null and the client fails.
$this->secret = env('STRIPE_SECRET');
// Correct: read the value the config file captured.
$this->secret = config('billing.secret');
}
}
echo config('billing.secret') === null ? 'missing' : 'configured', PHP_EOL;
// configuredFailing loudly on a required variable
The helper itself never complains about a missing key, which means a typo in a variable name surfaces as a confusing null far from its cause. Env::getOrFail() turns that into an immediate, named RuntimeException while the config files are being loaded.
<?php
use Illuminate\Support\Env;
// config/services.php
return [
'webhook' => [
// Optional: falls back silently.
'timeout' => (int) env('WEBHOOK_TIMEOUT', 10),
// Required: throws RuntimeException if the variable is unset.
'signing_key' => Env::getOrFail('WEBHOOK_SIGNING_KEY'),
],
];
try {
Env::getOrFail('WEBHOOK_SIGNING_KEY');
} catch (RuntimeException $e) {
echo $e->getMessage(), PHP_EOL;
}
// Environment variable [WEBHOOK_SIGNING_KEY] has no value.
Discussion