Stop Calling env() Outside Your Config Files
This is one of the most common ways to get burned deploying a Laravel app, and it passes every test in development. You reach for env('SOME_KEY') inside a controller or service, it works locally, you ship it, and in production it silently returns null. The cause is config caching, and the rule that avoids it is simple: only call env() inside config/ files.
What Goes Wrong
For performance, production deployments almost always run php artisan config:cache. This compiles every config file into a single cached PHP array so the framework does not have to read and parse dozens of files on each request.
The critical side effect is documented plainly: once the configuration has been cached, the .env file will not be loaded, and all calls to the env() function for variables outside of your configuration files will return null. The env() function only reads real environment variables, and after caching, those variables are no longer populated from .env.
So this code is a trap:
// In a controller or service. Returns null in production after config:cache.
public function client(): StripeClient
{
return new StripeClient(env('STRIPE_SECRET'));
}
It works in development only because you have not cached your config there.
The Fix
Read the environment variable once, in a config file, and reference the config value everywhere else. Add an entry to a config file:
// config/services.php
return [
'stripe' => [
'secret' => env('STRIPE_SECRET'),
],
];
Then use config() in your application code:
public function client(): StripeClient
{
return new StripeClient(config('services.stripe.secret'));
}
Now config caching works with you instead of against you. The env() call lives in the config file, where it runs at cache-build time, and your code reads the cached config value, which is always available.
The Mental Model
Think of .env as build-time input that feeds config/, and config() as the runtime interface your app talks to. Environment variables configure the config; the application reads the config. Keep env() on the config side of that line and this entire category of production-only bug disappears.
A quick way to audit an existing project is to grep for env( across your app/ directory. Every hit outside of config/ is a latent bug waiting for someone to run config:cache.
grep -rn "env(" app/
Move each one into a config file and you have inoculated the project against a genuinely nasty deployment surprise.
Further Reading
- Laravel documentation, "Configuration", Configuration Caching (laravel.com/docs/13.x/configuration#configuration-caching)
- Laravel documentation, "Configuration", Accessing Configuration Values (laravel.com/docs/13.x/configuration#accessing-configuration-values)