larament/dot-env-editor
Edit Laravel .env files programmatically with larament/dot-env-editor. Read, update, add, and remove environment variables safely while preserving comments, formatting, and key order. A simple API for managing dotenv values at runtime without clobbering your file.
Install via Composer and the service provider/facade are auto-discovered — no manual config/app.php edits on Laravel 9+:
composer require larament/dot-env-editor
If the package ships a config file, publish it before you need to override the env path or backup behaviour:
php artisan vendor:publish --tag=dotenv-editor
The facade is the first place to look. A typical first use is a settings screen that persists a single non-critical value:
use Larament\DotEnvEditor\Facades\DotEnvEditor;
DotEnvEditor::setKey('APP_NAME', $request->input('app_name'))->save();
Treat the README's method list as the source of truth for the exact fluent chain (setKey/setKeys/deleteKey/save), but the mental model is always the same: mutate an in-memory representation of .env, then explicitly call save() to write it back. Nothing is written until save() — that single fact shapes most of the patterns below.
Wrap it in one service, not scattered calls. The package is deliberately dumb; your app shouldn't be. Create App\Services\EnvWriter that validates keys, whitelists which keys are editable, and centralises the post-write cache clear:
class EnvWriter
{
public function put(array $values): void
{
DotEnvEditor::setKeys($values)->save();
// .env is now stale in config cache — always follow up
Artisan::call('config:clear');
}
}
Batch, don't loop. Always call setKeys([...])->save() for a group of related values (mail host + port + username + password) so you only touch the file once and avoid a half-written state if one write throws.
Admin settings controller pattern. Pair the writer with a FormRequest and a strict allowlist. A good shape:
public function update(SettingsRequest $request, EnvWriter $env)
{
$env->put($request->validated()); // validated() only contains whitelisted keys
return back()->with('status', 'Settings saved.');
}
Never pass $request->all() — you'd let a user overwrite APP_KEY or DB_PASSWORD.
Installer / first-run wizard. This is the package's strongest fit: collect DB credentials on a form, write .env, then re-establish the connection before continuing:
$env->put([
'DB_DATABASE' => $data['database'],
'DB_USERNAME' => $data['username'],
'DB_PASSWORD' => $data['password'],
]);
config(['database.connections.mysql' => array_merge(
config('database.connections.mysql'),
['database' => $data['database'], 'username' => $data['username'], 'password' => $data['password']]
)]);
DB::purge('mysql'); // force reconnect with the new credentials
Note the deliberate use of config([...]) here — you cannot rely on env() after the file is rewritten in the same request (see Gotchas).
Pair with a backup strategy. If the package exposes backup/restore helpers, call a backup before every write and prune old backups (a scheduled command deleting backups older than 7 days). If it doesn't, snapshot the file yourself with Storage::disk('local')->put('env-backups/'.now()->timestamp, file_get_contents(base_path('.env'))) before writing.
Feature-test it without touching your real .env. Point the package at a fixture by overriding the path in setUp() (config key or constructor, depending on the published config), then assert against the fixture contents. Never let the test suite write to base_path('.env').
Queue the "after" work. Post-write tasks — clearing config cache, notifying admins, restarting Horizon — belong in a job or listener so the HTTP request stays fast and a failed cache clear is retried.
config:cache, env() returns null everywhere because Laravel no longer reads the file. Rewriting .env at runtime does not undo this — you must php artisan config:clear (or re-cache) and, in long-running workers, restart them. Bake this into your EnvWriter so nobody forgets.config('mail.host') in application code; only use the package's read methods for building the admin form itself. Otherwise you're coupling runtime code to a file that may not even be readable.#, =, or $ must be quoted. Verify whether the package quotes automatically — many "simple" env editors don't, and an unquoted P@ss#word silently truncates at the #. Wrap values yourself if the package doesn't..env parsing. Store those in a file/secret manager and reference the path instead.www-data needs write access to .env, which is also your secrets file. That's a genuine security trade-off — prefer running env-writing features only in installers or behind app()->isLocal(), and never expose an arbitrary-key write endpoint.Cache::lock('env-write') and keep the critical section short..env sections, verify behaviour against a real file before shipping.\n only, keys may end up with a stray \r. Keep .gitattributes sane and check the written file after the first run..env, add them to .gitignore immediately — a backup containing production secrets in a repo is a bad day.save()? (2) is .env writable by the PHP user? (3) is config:cache still active? (4) did you check the actual file contents rather than env() in the same request? Those four cover nearly every report.EnvWriter and log the diff of getContent() before and after.How can I help you explore Laravel packages today?