rudnev/laravel-settings
A Laravel package for storing and managing application settings in the database. It provides a simple API to read and write settings at runtime, making it easy to add editable configuration options without changing env files.
Architecture fit. rudnev/laravel-settings occupies a well-defined and genuinely useful niche: runtime-editable application settings that would otherwise live in config/*.php and require a deploy to change. The package provides a single key/value table, a Settings::get() / Settings::set() facade, type-aware serialization (string/number/bool/array), and a cache with invalidation on write. That is the correct shape for a "database-backed settings store" and matches what most teams end up hand-rolling when they hit their first "the business wants to change a feature-flag/threshold/email-from address without waiting on a release."
The important architectural caveat for a TPM is that this package is the storage primitive, not the product. It gives you a facade and a table; it does not give you an admin UI, an audit trail, per-tenant scoping, validation, or a permissions model. "Editable at runtime" still requires someone or something to call Settings::set() — typically an in-house admin screen, an artisan command, or a support-ops tool. That downstream admin surface is real scope and should be budgeted separately.
Where it fits well: operator-controlled, low-cardinality, global application settings — feature toggles, rate-limit thresholds, notification sender addresses, maintenance-mode copy, feature rollout percentages.
Where it does not fit: secrets and credentials (should be in a secrets manager), per-tenant configuration at scale (the package is a global key/value store; multi-tenancy is your problem to solve), high-write-frequency data (the cache invalidation is on write; fine for settings, wrong for counters), and any setting that requires an audit trail or approval workflow (not provided).
Integration feasibility. High. It follows standard Laravel package conventions — Composer install, service-provider auto-discovery, vendor:publish for config, and a bundled migration. A competent Laravel engineer can have it working in an afternoon. The API surface is small enough to read in full before adopting, which I would recommend.
Technical risk. The dominant risk is not technical but stewardship. The observable signals are thin: 7 stars, 0 dependents, last release March 2024. The 2.4.0 release added Laravel 11 support — but Laravel 12 shipped in early 2025, so the package is now at least one framework major behind the current release. A single-maintainer, low-visibility utility package on a slow cadence is a real bus-factor concern if it sits on a critical path.
That concern is heavily mitigated by two facts: the license is MIT and the codebase is small. The realistic mitigation is fork-and-patch — vendor the package, apply the Laravel-12 constraint bump and any deprecation fixes yourself if upstream goes quiet. Budget a day for an initial review and roughly half a day per Laravel major thereafter. That is a manageable number; it is not zero.
Key questions to resolve before adoption.
Stack fit. Laravel 9/10/11 are supported via 2.4.0. Laravel 11 requires PHP 8.2+. If the host application is on Laravel 11 or 12, plan for the possibility that 2.4.0 is the newest compatible release and that the Laravel 12 constraint may need a fork or a composer.json override. Applications on Laravel 9 should confirm 2.4.0 did not narrow the constraint — a common failure pattern is a "framework support" release that widens for the new version and drops the old. Verify against the tag diff, not the changelog line.
Migration path from config files. The package does not migrate your existing config/*.php values; you do. The workable pattern is: (1) leave existing config/*.php defaults in place as the authoritative fallback, (2) publish the package config and migration, (3) run the migration, (4) seed the table with values lifted from existing config in a one-time seeder, (5) change call sites from config('foo.bar') to Settings::get('foo.bar', config('foo.bar')). That last pattern — passing the config value as the default argument — is the safety net and should be a convention enforced in code review, because it means a missing setting degrades to previous behavior rather than null.
Compatibility.
file or array. If the app runs more than one instance, a file-backed cache produces the classic "I changed the setting and it only took effect on one node" bug. This should be an explicit deployment checklist item."false", "0", and numeric strings. Write a small test suite around round-tripping each supported type before using it for anything business-critical.Sequencing.
Settings::set() and Settings::get() for each type, confirm cache invalidation.App\Support\Settings) that delegates to the package. This is the single highest-leverage integration decision — it gives you a one-file swap if you later fork, replace, or extend the package with per-tenant scoping.Maintenance. Recurring cost is small but real: one compatibility review per Laravel major (roughly annually), plus a fork-and-patch contingency if upstream stops tracking releases. Keep the package in the dependency register with a named owner, the current pinned version, and a "last reviewed" field. Set a calendar reminder two months ahead of each Laravel major release to check the upstream constraint. Assume the package may be a fork by the second Laravel major after 2024; that is fine, it just needs an owner.
Support. Single maintainer, MIT, no commercial support, no observable cadence guarantee. That is acceptable for a utility of this size provided the fork option is written down somewhere and the package is not on a path where a week of unreleased incompatibility would be catastrophic. It should not be used for anything where a broken settings read takes down the site — always supply the config default so a read failure degrades.
Scaling. Read performance is fine because reads hit the cache; the DB is only touched on a miss. Three scaling concerns to flag:
Failure modes to plan for.
"true" and read as a boolean will silently misbehave. Enforce types in the wrapper, not at call sites.updated_by column in your own migration.Ramp-up. Near zero for an engineer fluent in Laravel conventions — the facade is one function call. The higher cost is on the product and ops side: deciding which settings should be runtime-editable (versus versioned in config), building the small admin surface so non-engineers can safely edit them, and giving support a documented, permissioned way to change settings during an incident without SSH access to a database. That is where the majority of the total cost of ownership actually sits — not in the package integration, but in the operational wrapper around it.
Recommendation. Adopt as a small, wrapped utility, not a platform dependency. The implementation cost is low and the value is real, but three conditions should be met before it goes into production: (1) a thin in-house App\Support\Settings wrapper that supplies config defaults, enforces types, and logs missing keys; (2) a shared cache store confirmed in the production deployment checklist; (3) a documented fork-and-patch contingency and a named owner in the dependency register. Do not store secrets in it, do not attempt multi-tenant scoping in the table itself, and do not adopt it for settings on the critical request path without the config-default fallback in place. With those guardrails, the low stars and thin release cadence are survivable; without them, the maintenance risk is being transferred to whoever is on call when upstream goes quiet during a framework upgrade.
How can I help you explore Laravel packages today?