config
Reads and sets configuration values by dot notation through the container's repository.
config(array<string, mixed>|string|null $key = null, mixed $default = null): mixedParameters
| Parameter | Type | Description |
|---|---|---|
$$keyoptional | array<string, mixed>|string|null= null | A dot-notation key to read, an array of key/value pairs to set, or null to get the repository itself. |
$$defaultoptional | mixed= null | Returned when the key is absent; ignored when setting. |
Return value
mixedThe configuration repository when no key is given, the value at the key when a string is given, or null when an array of values is set.
Errors
Throws Illuminate\Contracts\Container\BindingResolutionException when no 'config' binding exists, which is the case outside a booted application.
See also
config() is a shortcut to the Illuminate\Config\Repository bound in the service container. With no arguments it hands you the repository itself. With a dot-notation string it reads a value, optionally falling back to a default. With an array it sets values and returns null.
What trips people up
Setting is in-memory only. config(['app.debug' => true]) changes the value for the current request or command and nothing else — no file is written and the change is gone next request. It exists for tests and one-off overrides.
The first key segment is the file name in config/, not a namespace: config('services.stripe.key') reads config/services.php.
Once php artisan config:cache has run, every config file is compiled into one array and env() stops returning anything outside those files. Code that called env() directly and worked locally then returns null in production — the classic deploy-only bug. Read the value through config() instead.
Finally, a missing key returns the default rather than raising, so a typo in a key looks exactly like a legitimately empty setting.
Example
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
class StripeClient
{
public function charge(int $cents): array
{
$key = config('services.stripe.key');
$currency = config('services.stripe.currency', 'usd');
echo "charging {$cents} in {$currency}\n";
return Http::withToken($key)
->post('https://api.stripe.com/v1/charges', [
'amount' => $cents,
'currency' => $currency,
])
->json();
}
}When to use it
- Read a third-party API key from config/services.php inside a service class.
- Override a setting for the duration of a single test.
- Fetch a value with a fallback when the config key may be absent.
Frequently asked questions
How do I read a config value in Laravel?
Call config('file.key') where the first segment is the filename in config/ — config('services.stripe.key') reads config/services.php. Pass a second argument as the fallback for a key that may not exist.
Does config() write changes to the config file?
No. config(['app.debug' => true]) sets the value on the in-memory repository for the current request or command only; the file on disk is untouched and the change is gone on the next request. Use it for tests and temporary overrides.
Why does env() return null in production but config() works?
Because php artisan config:cache compiles the config directory into one array and stops loading the .env file, so env() calls outside config/ have nothing to read. Move the env() call into a config file and read it back through config().
More examples
Reading, defaulting, and the repository itself
The has() call is the only reliable way to tell a missing key from a key whose value is genuinely null, because config('app.nonexistent') returns null in both cases. Reaching for the repository is also how you get prepend() and push() for array config values.
<?php
// Read a value by dot notation: config/app.php, key 'timezone'.
$timezone = config('app.timezone');
// Supply a fallback for a key that may not be defined.
$currency = config('services.stripe.currency', 'usd');
// No arguments gives you the repository, which has extra methods.
$repository = config();
echo $timezone, "\n"; // UTC
echo $currency, "\n"; // usd
var_dump($repository->has('app.timezone')); // bool(true)
var_dump($repository->has('app.nonexistent')); // bool(false)Overriding config inside a test
The array form sets values on the in-memory repository only, so the override lasts for this test method and Laravel's test case rebuilds the application between tests. Nothing touches config/services.php on disk.
<?php
namespace Tests\Feature;
use Tests\TestCase;
class BillingTest extends TestCase
{
public function test_it_uses_the_configured_currency(): void
{
config(['services.stripe.currency' => 'eur']);
echo config('services.stripe.currency'), "\n"; // eur
$this->assertSame('eur', config('services.stripe.currency'));
}
}Why env() breaks after config:cache
config:cache compiles every config file into a single array and stops loading the .env file, so env() outside config/ has nothing to read. The failure only appears in production, which is why calling env() directly in application code is the most common configuration bug in Laravel.
<?php
// config/services.php — the only place env() is safe to call:
//
// return ['stripe' => ['key' => env('STRIPE_KEY')]];
// In application code, after `php artisan config:cache` has run,
// this returns null in production even though .env has the value:
echo var_export(env('STRIPE_KEY'), true), "\n"; // NULL
// Read it through the compiled config repository instead:
echo var_export(config('services.stripe.key'), true), "\n"; // 'sk_live_...'
Discussion