Aug 19, 2026MediumEnglish
I Got Tired of Sharing `.env` Values Over Slack
Why local development environment variables still fall through the cracks of otherwise structured engineering workflows — and the idea behind local.env.
There is a small problem I have seen in almost every development team I have worked with. Someone adds a new environment variable, updates .env.example, opens a pull request, and eventually merges it. A few hours later another developer pulls the latest changes and the application stops working because their local environment is missing the new value. What follows is usually a Slack message asking where the value is, followed by a DM, a password manager link, an old .env file, or somebody copying the value directly into the conversation.
None of this feels serious enough to justify a large infrastructure project. It is usually solved in a minute or two, which is probably why teams tolerate it for years. But it keeps happening. New developers join, people switch machines, repositories gain more services, environment variables change, and the same small interruption repeats itself. Eventually there is also uncertainty around which copy is current, whether everyone received the new value, and whether somebody pasted something into a place where it should not have been pasted.
What bothered me most was that the rest of the development workflow had already become quite structured. Git tells us exactly which version of the code we have. Package managers describe the dependencies required to run it. Database migrations describe changes to persistent state. CI verifies that a pull request satisfies a set of expectations before it gets merged. Local environment variables, however, often remain a piece of state that exists mostly through convention and team memory.
A committed .env.example helps, but only partially. It tells me that STRIPE_SECRET_KEY now exists, but it does not tell me how my machine should obtain the value or whether the version I already have is still the one the team expects. The schema is part of the repository while the actual local state is managed through a completely separate, usually informal process.
I started building local.env around that gap.
The idea is fairly simple. The repository describes which environment variables the local development environment requires, while the values themselves are kept outside Git and synchronized through a CLI. If a code change introduces a new requirement, that requirement travels with the code. A developer pulling the repository can then see what their local environment is missing and synchronize the values they are authorized to use instead of finding the right person to ask.
I intentionally did not try to make local.env a general-purpose secrets platform. Production credentials, CI secrets, Kubernetes secrets and service-to-service authentication are different problems with different operational and security requirements. There are already mature systems for those use cases. The problem I wanted to solve was much narrower: keeping developer machines aligned with the local environment expected by the codebase.
That narrower scope also influenced the security model. I did not want the easiest implementation to be “send all of your development secrets to my server and trust me.” Managed values are encrypted on the developer machine before being uploaded, and the server stores ciphertext together with the metadata needed to coordinate synchronization. Repository encryption keys are distributed to authorized devices rather than stored on the server in plaintext, so decryption happens on the developer side.
This does not make the system magically immune to compromise, and I think tools dealing with secrets should be careful about making claims like that. If a developer machine is compromised, the secrets available to that developer may be compromised as well. If someone has already learned a credential, revoking their device cannot make them forget it. Real credentials may still need to be rotated at their provider. The objective is much more concrete: the server should not need plaintext access to the values it is helping the team synchronize, and obtaining a copy of the database alone should not be enough to recover them.
I also decided early that the project should be open source and self-hostable. A tool that interacts with both GitHub and developer credentials asks for a level of trust that I would personally be uncomfortable giving to a black box. I want users to be able to inspect the encryption code, see exactly which GitHub permissions are requested, understand what is stored on the server, and run the entire thing themselves if that is what they prefer.
The first public version is now available, and I have reached the point where continuing to add features on my own would probably teach me less than watching other teams use it. My assumptions come from workflows I have personally seen, but environment management tends to be surprisingly different from one team to another. Some teams use 1Password, some keep a shared encrypted file, some have internal scripts, and some still send values through Slack because it is simply the fastest thing that works.
That is the part I am interested in now. I want to see what happens when local.env is installed on repositories I did not design it around. Whether the GitHub setup feels unnecessarily complicated, whether the CLI makes sense without explanation, whether the synchronization model works for monorepos and worktrees, and whether the problem itself is important enough for developers to change an existing habit.
If your team has ever had the familiar experience of pulling a branch and discovering that the missing dependency was not code but an environment variable nobody told you about, I would be interested in hearing how you currently solve it.
GitHub: https://github.com/mithatakbulut/local.env Docs: https://www.local.env.best
Originally published on Medium.
View on Medium