orchestra/config
Orchestra Config provides a Laravel-friendly configuration component for Orchestra Platform, helping packages and extensions register, merge, and manage config files through a consistent API. It integrates cleanly with Laravel’s configuration system.
Confirm the gate before you write any code. This package's major version tracks the Laravel major, and v6.0.0 exists to add Laravel 8 support. If the app is on Laravel 9+, Composer will not resolve and nothing application-side fixes it. Start with:
composer why-not orchestra/config ^6.0 # tells you the blocker in one line
composer show orchestra/config # PHP range and illuminate/* constraint
Install and read, in this order:
composer.json — the illuminate/* constraint is the compatibility statement. There is no upgrade guide, so the constraint is the contract.src/ — this is the whole package's behaviour in a few dozen lines.Repository class — almost certainly a subclass of Illuminate\Config\Repository. Everything else is a thin shell around it.The mental model. This is not a new config system. It rebinds Laravel's config container entry to a repository with extra surface, so config(), the Config facade, and every third-party package keep working unchanged. Your job is to not fight that.
First use case. Modular config with defaults plus app-level overrides. Your package ships config/billing.php defaults; the host app publishes and edits a copy; the app value wins. That precedence ladder — library default → package default → environment → tenant → runtime override — is the one thing worth writing down on day one and testing.
Rebinding the repository. Register after the framework's own config provider so the binding actually takes effect, and keep the swapped class thin:
$this->app->singleton('config', fn ($app) => new \Orchestra\Config\Repository(
new \Illuminate\Config\FileLoader($app['files'], $app->configPath()),
$app->environmentPath(),
$app->environment()
));
Anything that reads config during register() before this runs gets the stock repository. If a feature silently ignores your override, provider order is the first suspect.
Layering, not replacing. Prefer mergeConfigFrom() in your own module providers for defaults, and reserve this package for the cases where you need repository-level behaviour (runtime writes, per-tenant resolution, inspection). Two mechanisms doing the same merge is how you get drift.
Runtime overrides per request. The natural fit is tenant/brand/plan variation:
// in a middleware, before the request hits domain code
foreach ($tenant->configOverrides() as $key => $value) {
config([$key => $value]);
}
Under Octane or any long-lived worker, reset overrides per request — a scoped binding or a middleware terminate() that restores the original array. Otherwise tenant A's discount rate leaks into tenant B's response, and it will be the hardest bug of your quarter.
Feature flags. Config files are a fine home for kill switches and per-account enablement until you need an audit trail or an admin UI. Decide that explicitly: file-based flags have a deploy cost; DB-backed flags have a caching and permissions cost. Don't discover the answer during an incident.
Testing. Point the container at a plain Illuminate\Config\Repository in setUp() with a fixture array, and assert on resolved values, not on the presence of files. Add one test that asserts the key set of each published config file — that catches renamed keys, which is the failure mode this package is most likely to hand you.
Caching. php artisan config:cache flattens everything into a single array. Runtime overrides still work (they're in memory), but any override logic that runs at boot must run after the cached file loads. Always config:clear after installing or bumping the package.
config:cache at deploy time, usually in CI on a Friday.artisan serve hold stale config. Restart long-running processes after any config change; in dev this looks like "my change didn't apply."config()->tenant() helper) rather than adding global helper functions. Keep the parent contract intact so framework code and other packages keep working.config/ directory is already the answer, and adding a layering concept gives you a precedence order to get wrong for no benefit.How can I help you explore Laravel packages today?