Skip to main content

Command Palette

Search for a command to run...

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.

Updated
10 min readView as Markdown
Stop Sending .env Files Around: I Built a Tiny CLI Instead
S
👋 Hey there! I'm a passionate developer with a knack for creating robust and user-friendly applications. My expertise spans across various technologies, including TypeScript, JavaScript, SolidJS, React, NextJS.

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 .env could 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.