# Stop Sending .env Files Around: I Built a Tiny CLI Instead

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`.

```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:

```text
.env
  ↓
envlock encrypt
  ↓
.env.lock
  ↓
Git
```

Your teammate pulls the repository and does the opposite:

```text
.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:

```text
.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:

```shell
npm install -g @sazzadur/envlock
```

Then go to your project and run:

```shell
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:

```text
.env
```

you'll end up with:

```text
.env
.env.lock
```

The real `.env` stays ignored.

`.env.lock` goes into Git.

```shell
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:

```shell
npm install -g @sazzadur/envlock
```

They clones the project:

```shell
git clone <repository>
cd <project>
```

Then:

```shell
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:

```env
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:

```shell
envlock encrypt
```

That updates `.env.lock`.

Then commit it normally:

```shell
git add .env.lock
git commit -m "update environment variables"
git push
```

Everyone else pulls the latest changes and runs:

```shell
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:

```shell
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:

```shell
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:

```markdown
+ 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:

```plaintext
.env
.env.dev
.env.staging
.env.production
```

Envlock keeps the suffix when it creates the encrypted version:

```text
.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:

```shell
envlock encrypt-all
```

and:

```shell
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`.

```shell
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:

```shell
npm install -g @sazzadur/envlock
```

```shell
envlock init
```

```shell
git add .env.lock .gitignore
git commit -m "add encrypted environment"
git push
```

For someone joining the project:

```shell
npm install -g @sazzadur/envlock
```

```shell
git clone <repository>
cd <project>
```

```shell
envlock decrypt
```

When the environment variables change:

```shell
envlock status
envlock diff
envlock encrypt
```

```shell
git add .env.lock
git commit -m "update environment variables"
git push
```

And everyone else:

```shell
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`.

```shell
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.
