Every Laravel app has the same three-way split: your laptop, a staging server, production. Each needs its own .env, and the .env itself must never be committed - What Not to Commit covers why. What is worth deciding deliberately is how the values get to each environment and how the team learns about new keys.
.env.example is the contract#
Commit .env.example with every key and a safe placeholder value, and treat it as part of every pull request that adds configuration:
MAIL_MAILER=
QUEUE_CONNECTION=
INVOICE_SIGNING_KEY=
A new hire runs cp .env.example .env, fills in the blanks, and the app boots. A reviewer sees the new key in the PR diff without seeing its value. When someone adds a key to their local .env without updating the example, the next person’s setup breaks quietly - so make “PR adds a key to .env.example” as automatic as “PR includes a migration”.
Getting values to servers#
Servers have no checkout step where you can edit a file by hand, so pick a delivery mechanism:
- The deploy tool’s secrets store (most managed deploy platforms have one).
- A file written by your deploy script from your password manager or vault - never from the repo.
- On a self-hosted box you administer: a plain
.envin the shared directory of your releases structure, permissions600, owned by the deploy user.
All three share one property: the value’s home is outside Git, and changing it does not require a commit.
After changing values#
Laravel caches config aggressively in production, so a changed .env does nothing until you rebuild the cache:
php artisan config:cache
Fold that into the deploy script and “I changed it but nothing happened” stops being a genre of bug report. Also restart the queue workers - they hold the old config in memory, same lesson as the deploy post’s queue:restart.
The audit habit#
Once a quarter, diff each environment’s keys against .env.example: keys present on servers but missing from the example are undocumented config, and keys in the example that no environment sets are dead weight. Two minutes with comm on sorted key lists keeps the contract honest.
Secrets stay out of history - and if one ever lands there, A Secret Got Committed. Now What? is the cleanup drill.
Share this post
Keep reading
A First CI Pipeline for a Laravel Project
Install, lint, test, build - four jobs that run on every pull request and turn 'works on my machine' into a check everyone can see.
A Staging Environment Worth Having
Staging only catches what it shares with production. Same deploy path, same services, sanitized data - and a clear rule for what it is for.
Branch Naming Conventions That Scale Past Three People
Branch names are a user interface for your team's work. The conventions that survive growth, the ones that collapse, and why boring always wins.