Why I Trust direnv — and the Time It Gave Me Someone Else's AWS Keys

I switched to direnv for per‑project environments. It saved me hours, but also once loaded a teammate's prod keys. How I changed my workflow to keep the convenience without the risk.

Written by: Arjun Malhotra

Laptop on a desk with a coffee cup, terminal visible on the screen
Photo by Xavier Wendling on Unsplash

It was 10:30 on a Monday and I was debugging a flaky payment flow that only failed on my coworker’s machine. He sent me a single message: “just run the tests, it’s fine locally.” I pulled his branch, ran direnv allow, and watched his shell silently pick up a dozen environment variables. A minute later my local terraform command ran using his AWS keys and began planning resources against our staging account.

I hit ctrl‑c. My heart raced. AWS bill spikes are a real thing. We were lucky — nothing was provisioned. But a few API calls had been made; CloudTrail showed them tied to my laptop’s IP. I could have avoided that 20‑minute scramble if I’d treated .envrc like a code change instead of magic.

This is the honest story of why I use direnv everywhere now — and the exact rules I ended up living by after that mistake.

Why direnv felt like magic

Before direnv, I juggled:

Direnv fixes that simple pain in one way I still appreciate: it auto‑loads/unloads project environment when you cd in and out. No more “which node is this using?” or “oh, I forgot to set DB_URL.” On a 6‑hour debugging day this saves dozens of tiny context switches.

Practical wins I actually felt in day‑to‑day work:

And in India the bandwidth win is real. When CI or a coworker asks for a repo clone, they don’t waste time copying env files over slow home connections — they run direnv allow after pulling an audited .envrc and get what they need.

The failure that changed my rules

What broke was simple: my colleague’s .envrc used a local include pattern to source ~/.secrets. His machine was set up to copy staging keys there for convenience. I trusted his repo, ran direnv allow without reading the .envrc, and suddenly my environment had his staging AWS credentials.

Two learnings from that hour-long panic:

  1. direnv’s convenience becomes a liability if you blindly allow .envrcs.
  2. Secrets should never be implicitly pulled from outside the repo by .envrc without an explicit, reviewed step.

I could have avoided the whole thing by reading a 6‑line file. But we don’t always read. So I changed the workflow.

My practical, non‑ideal rules (that actually stuck)

I hacked together a small, enforceable workflow — opinionated and slightly annoying, but worth it.

  1. .envrc is code. Treat it like code.
  1. Keep secrets out of .envrc.
  1. Use allowlist patterns and a repo template.
  1. Automate basic safety checks.
  1. Keep a manual escape hatch for quick tests.

Tradeoffs I accepted

When direnv still fails me

One limitation I ran into: on our office shared servers and some CI runners with locked shells, direnv needs a shell hook; that doesn’t always play nicely with limited /bin/sh setups. On those machines I keep a small wrapper script that sources a vetted env file instead. In other words: direnv is great for personal and laptop‑based dev, less so for heavy restricted environments.

The one thing I actually walked away with

Direnv is fast and delightful. But magic that silently changes your environment is the kind of convenience that bites on the worst day. The single habit that prevented a repeat for me: never run direnv allow without scanning the .envrc for external includes and secrets. Make it a code review item, and automate a linter to make doing the right thing the default.

If you want my onboarding script and the tiny pre-commit linter I use, ping me — it’s about 150 lines and saved us a lot of heartburn.