Skip to content

Why We Killed the .env File

Every developer tool today has a .env problem. Whether it’s .env, .env.local, or .env.something-else, dev tools inevitably want to dump your application’s secrets into a plaintext file sitting on your hard drive.

It’s a security nightmare. We’ve all seen the consequences: secrets leaking into git history, sitting on unencrypted developer laptops, or getting caught up in random backups. When you ask your CISO if you can use a new local dev tool, they look at this .env sprawl and immediately say no.

Enterprise security teams—the folks dealing with SOC2, HIPAA, and FedRAMP—routinely block these tools for a reason. The fundamental issue is that development tools treat secrets as static files instead of what they really are: runtime context.

We realized that to build a secure local development experience for Kubernetes, we couldn’t rely on files. So we killed the .env file entirely.

The industry has mostly settled on workarounds rather than solutions. If you look at the landscape of local Kubernetes dev tools, they almost universally rely on secrets written to disk in some form.

Tool Approach The Problem
Telepresence Volume mounts + env file export Secrets land in files on disk
docker-compose .env file mapping Secrets are plain text files
Tilt / Skaffold Build-time env injection Secrets end up in build context or logs
Doppler / Infisical CLI wrappers Closest to our approach, but still requires another tool

Each of these approaches introduces friction or security holes. We wanted something better.

Here’s what it looks like when you run a service locally with Diverge:

Terminal window
diverge dev --service payments -- go run ./cmd/server

That’s it. Notice what’s missing? There are no .env files to download, no exports, and no magic wrappers. Under the hood, Diverge is doing something much more sophisticated:

  1. Discovery: It finds the baseline pod for your service running in the cluster.
  2. Resolution: It reads the pod’s environment specification. This isn’t just literal values—it dynamically resolves SecretKeyRef, ConfigMapKeyRef, and EnvFrom directly from the Kubernetes API.
  3. Injection: It injects these resolved values directly into the child process’s memory space. The secrets never touch your hard drive.
  4. Isolation: It uses process group isolation (syscall.Setpgid) to ensure clean teardown when you’re done.

Here is how the flow looks:

sequenceDiagram
    participant Dev as Developer
    participant Div as Diverge CLI
    participant K8s as Kubernetes API
    participant Proc as App Process
    
    Dev->>Div: diverge dev -- go run
    Div->>K8s: Get Pod env spec
    K8s-->>Div: Returns env refs
    Div->>K8s: Resolve Secrets & ConfigMaps
    K8s-->>Div: Returns decrypted values
    Div->>Proc: Fork/Exec with env in memory
    Proc-->>Dev: Running application

No .env file. No .gitignore entry. No SOC2 finding.

We built the security model around strict isolation and fail-fast mechanics.

First, Diverge respects your RBAC. If your local user doesn’t have permissions to read a specific secret in the cluster, Diverge crashes immediately. We don’t try to silently skip it or provide an empty value. Fail fast, fail loud.

You might be wondering why we don’t support something like diverge dev --env-output eval. The reason is simple: shell history leaks. If we evaluate secrets into your shell, they end up in your ~/.zsh_history or ~/.bash_history. By forcing execution through our wrapper, we keep secrets out of your shell entirely.

We also implemented process group isolation. When you hit Ctrl-C to stop your local development session, Diverge sends a signal to the entire process tree. This ensures no orphaned child processes are left running in the background with access to sensitive environment variables.

Finally, Diverge detects volume-mounted secrets. While we can’t inject files into memory the same way we do environment variables, we provide clear warnings about any volume-mounted secrets we can’t automatically resolve.

“But what about my debugger?” is the first question we hear when we tell people we don’t use .env files. Don’t worry, we’ve got you covered.

Because Diverge acts as an executable wrapper, it integrates cleanly into any standard IDE.

In VS Code, simply use Diverge as the target executable in your launch.json:

{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Payments Service",
"type": "go",
"request": "launch",
"mode": "exec",
"program": "diverge",
"args": ["dev", "--service", "payments", "--", "go", "run", "./cmd/server"]
}
]
}

For JetBrains IDEs, you can set Diverge up as an External Tool or wrap it in a Run Configuration:

<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run with Diverge" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="payments" />
<working_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<package value="github.com/divergedev/payments/cmd/server" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/cmd/server/main.go" />
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Diverge Setup" run_configuration_type="ShConfigurationType" />
</method>
</configuration>
</component>

While keeping secrets in-memory is primarily a security win, it’s part of a larger philosophy about environment efficiency.

Because Diverge resolves state dynamically from the cluster, your local environment is always in sync with your preview environment. And thanks to our integration with KEDA, those preview environments scale to zero when you aren’t actively using them.

Your preview environments cost $0 when idle. You get the security of in-memory secrets and the cost efficiency of scale-to-zero infrastructure. Learn more about how we do this on our Scale to Zero concepts page.

Ready to stop managing .env files? You can get started with Diverge today.

Terminal window
curl -sL https://divergedev.com/install.sh | bash
diverge dev --service my-app -- npm run dev

Check out the Quick Start guide to see it in action, or star us on GitHub.