Stop Sending .env Files Around: I Built a Tiny CLI Instead
How a small team annoyance turned into Envlock, a simple way to keep encrypted .env files in Git.

Almost every project has a .env file sitting somewhere with database URLs, API keys, tokens, secrets, and all the other things you definitely don't want ending up in a public GitHub repository.
So one of the first things we usually do is add it to .gitignore.
.env
.env.*
And that works. Git doesn't get your secrets, nobody accidentally pushes an API key, and everything is fine.
Until someone else needs to run the project.
The code is there, the dependencies are there, the README is there, but the environment variables are not.
And then comes the message:
“Can you send me the latest
.env?”
That one question is basically why I ended up building Envlock.
The Problem Wasn't .gitignore
Keeping .env out of Git is easy. Sharing it with the rest of the team is the annoying part.
You send the file once, everyone gets the project running, and you forget about it.
Then a few days later someone adds a new API key.
Someone else changes a database URL.
Another person still has the copy you sent two weeks ago.
Before long, there are multiple slightly different versions of the same .env floating around, and nobody is completely sure which one is actually the latest.
It's not some huge engineering disaster.
It's just one of those small problems that keeps showing up again and again.
We did the right thing by keeping secrets out of Git. We just moved the problem somewhere else.
Surely There Had to Be a Better Way
Before building anything, we tried the obvious stuff.
For a while, we kept our .env files in Google Drive. It worked. Whenever someone needed the environment variables, they could just go to Drive and download the file.
But then you have another version of the same problem.
Someone updates their local .env, but forgets to update the one in Drive. Or you open the folder and start wondering whether the file there is actually the latest one. The code is version-controlled in Git, while the configuration needed to run that code is sitting somewhere completely separate.
We also tried sharing secrets through tools like Bitwarden and 1Password. They're great at what they're built for, especially passwords and individual secrets, but sharing an entire .env file and keeping it in sync with a project still felt a little awkward for our workflow.
There are other ways to solve this too. You could share an encrypted archive, keep environment variables in a private document, use a dedicated secrets manager, or manually maintain the values from an .env.example.
All of them can work.
But I kept coming back to one thought: we already have a system that is really good at keeping files in sync across the team.
Git.
The only reason we couldn't use it for .env was because the file contains secrets.
So instead of finding another place to store the .env, I started wondering:
What if the
.envcould travel with the repository, just not as plain text?
That was really the idea I wanted.
Keep the actual .env ignored, encrypt it, commit the encrypted version, and let anyone on the team with the password decrypt it locally.
No separate folder to check. No copying individual secrets around. And when the environment changes, the encrypted file changes alongside the code.
That thought eventually became Envlock.
Meet Envlock
Envlock is a small CLI tool that sits between your .env file and Git.
You keep the real .env ignored, Envlock creates an encrypted version of it, and that encrypted file can safely live in the repository.
The basic idea looks like this:
.env
↓
envlock encrypt
↓
.env.lock
↓
Git
Your teammate pulls the repository and does the opposite:
.env.lock
↓
envlock decrypt
↓
.env
That's pretty much the whole idea.
No dashboard, no account to create, no server to run.
Just a CLI.
How Envlock Works
Envlock takes the contents of your .env file, encrypts them using a password, and writes the encrypted data into a .env.lock file.
Your project ends up looking something like this:
.env # ignored
.env.lock # committed
package.json
src/
The actual environment variables never go into Git. Only the encrypted file does.
When someone else pulls the repository, they get .env.lock, but they still need the password to decrypt it.
So the encrypted file can move through Git normally, while the password is shared separately with the people you trust.
Instead of sending the complete .env again every time something changes, you only need to share the password once.
Git takes care of the rest.
Setting It Up
Getting started is pretty straightforward.
First, install Envlock globally:
npm install -g @sazzadur/envlock
Then go to your project and run:
envlock init
Envlock looks for .env files in the project. If there is more than one, you can choose the one you want to initialize.
You'll be asked to set a password, and Envlock will create the encrypted version of the file.
If you start with:
.env
you'll end up with:
.env
.env.lock
The real .env stays ignored.
.env.lock goes into Git.
git add .env.lock .gitignore
git commit -m "add encrypted environment"
git push
At that point, the encrypted environment travels with the rest of the project.
Getting Your Teammate Up and Running
Now we get to the part I actually wanted to make easier.
Your teammate install Envlock:
npm install -g @sazzadur/envlock
They clones the project:
git clone <repository>
cd <project>
Then:
envlock decrypt
Envlock asks for the password and restores the .env file locally.
Done.
Instead of:
“Hey, can you send me the latest
.env?”
the onboarding instructions become:
Clone the repo, run
envlock decrypt, use the password I gave you.
You still have to share that password securely, of course. Envlock doesn't magically solve the problem of getting a secret from one human to another.
Sadly, I haven't invented that part yet.
But you only have to share one password instead of passing around a new .env file every time something changes.
When Someone Changes the .env Again
Because they will.
Maybe someone adds a new API key:
NEW_SERVICE_API_KEY=...
Or changes the database URL.
Or adds one of those environment variables from a "quick five-minute change" that somehow stays in the project forever.
Once the .env changes, you encrypt it again:
envlock encrypt
That updates .env.lock.
Then commit it normally:
git add .env.lock
git commit -m "update environment variables"
git push
Everyone else pulls the latest changes and runs:
envlock decrypt
Now everyone has the same version again.
No file attachments. No digging through old messages. No asking which .env is the latest one.
Did I Forget to Encrypt It?
There's still another very human problem.
You change .env.
You continue working.
You commit your code.
And somewhere around three hours later you remember:
Oh. I never updated
.env.lock.
That's why Envlock has:
envlock status
It lets you check whether the local environment file and its encrypted version are in sync.
And when something changed, you can inspect the difference with:
envlock diff
The useful part is that diff doesn't dump the actual secret values into your terminal.
Instead, you can see which variables changed:
+ NEW_API_KEY
~ DATABASE_URL
- OLD_SERVICE_TOKEN
So you know what happened without printing something you'd immediately regret during a screen share.
More Than One .env File
Not every project has one environment file.
A project might have something like:
.env
.env.dev
.env.staging
.env.production
Envlock keeps the suffix when it creates the encrypted version:
.env.dev → .env.lock.dev
.env.staging → .env.lock.staging
.env.production → .env.lock.production
You can encrypt or decrypt files individually, but there are also commands for processing all of them:
envlock encrypt-all
and:
envlock decrypt-all
This was important because Envlock isn't meant for any particular framework or language.
It doesn't care whether the project is written in JavaScript, Python, PHP, Go, or something else.
If the project uses .env files, that's really all Envlock needs to know.
What About CI/CD?
Typing a password into a CLI works perfectly fine on a laptop.
Your CI runner is slightly less cooperative.
For automated environments, Envlock can read the password from ENVLOCK_PASSWORD.
export ENVLOCK_PASSWORD="your-password"
envlock decrypt-all --force
In a real pipeline, you obviously shouldn't hardcode that password into the workflow file.
Store it using the secret storage provided by your CI platform and expose it to Envlock during the job.
Envlock takes care of decrypting the file.
Your CI provider takes care of protecting the password.
The robots get their .env too.
What Envlock Is Not
This part matters.
Envlock is not a replacement for a proper secrets management platform.
It doesn't give you per-user access control, detailed audit logs, automatic secret rotation, compliance controls, or protection from a malicious teammate who already has access to the password.
And the security model is pretty straightforward:
If someone gets both the encrypted files and the password, they can decrypt the secrets.
Envlock is meant for small, trusted teams where the alternative is usually something like:
“I'll send you the
.env.”
If you're managing highly sensitive infrastructure, need strict access controls, have compliance requirements, or need to revoke access for individual users, you should use a proper secrets management system.
There are bigger tools for bigger problems.
Envlock intentionally isn't one of them.
Why I Kept It Small
When I started working on this, I didn't want to turn it into another platform.
I didn't want accounts, servers, databases, dashboards, subscriptions, workspaces, or an admin panel.
The original problem was simple:
I have a .env.
My teammates need the same .env.
I don't want us sending it back and forth whenever something changes.
So Envlock stays local.
It encrypts files.
It decrypts files.
It tells you when something changed.
And then it gets out of the way.
Sometimes a tool doesn't need to become a platform.
Sometimes it just needs to fix the annoying thing that made you build it in the first place.
The Whole Workflow, Without the Drama
For the person setting up the project:
npm install -g @sazzadur/envlock
envlock init
git add .env.lock .gitignore
git commit -m "add encrypted environment"
git push
For someone joining the project:
npm install -g @sazzadur/envlock
git clone <repository>
cd <project>
envlock decrypt
When the environment variables change:
envlock status
envlock diff
envlock encrypt
git add .env.lock
git commit -m "update environment variables"
git push
And everyone else:
git pull
envlock decrypt
That's the workflow.
No hunting through old messages trying to figure out whether final.env, latest.env, or latest-final-actually-final.env is the one you're supposed to use.
Give It a Try
Envlock is available on npm as @sazzadur/envlock.
npm install -g @sazzadur/envlock
You can find it here:
https://www.npmjs.com/package/@sazzadur/envlock
It's still a small tool, and that's intentional.
If your team's current .env sharing process is basically someone asking for the latest file whenever something changes, Envlock might make that process a little less annoying.
And if you try it and find something that could be better, I'd genuinely like to hear about it.



