TLDR: You can build or use a pre-made CLI together with your application's start command to inject environment variables for local development and avoid using a
.env file altogether.
Recently, I wrote about fetching environment variables for applications so teams always have access to the right variables. The method was simple: store environment variables in a secret management platform, then have the application fetch them back and cache them at runtime. This, however, came with a nuance of having to manage yet another token to fetch these variables.
In this article, however, I discuss an enhanced approach specifically for local development to inject environment variables into an application without needing a
.env file at all.
It sucks to fetch environment variables back for an application if the method still requires managing one environment variable
This is a common complaint and debate amongst developers considering to use a secret management platform to store their environment variables only to find out that they still need to manage a token to gain access to the platform.
While the approach of storing a token in a
.env file and using it to fetch back environment variables tamed secret sprawl, it did little to improve on the security front. After all, you could still leak the token to source control and, if not revoked, bad actors could use it to access your environment variables.
I'm glad to say, however, that you can use a secret management platform and fetch environment variables back without needing to store another proxy token. Here's how:
Let me explain.
It turns out that your application running in local development is just a process and it's possible to pass environment variables into that process as it starts up. This means that you can build a platform-agnostic CLI to fetch back environment variables from a centralized source and wrap it around your application's start command. Couple the CLI with authentication logic and proper credential storage on your system and you can have a secure workflow that pulls and injects environment variables into your local development process.
Moving forward, members of your team only need to install the CLI onto their machine, authenticate with the secret management platform via the CLI, and start up their application via CLI with environment variables injected - Wizardry.
While you can code the CLI yourself and connect it to an existing secret management platform, I wouldn't recommended it for most developers since this isn't a quick endeavor. Instead, I'm going to be using the Infisical CLI to pull and inject environment variables in local development from Infisical, a popular open-source, end-to-end encrypted secret management platform that you can store environment variables with.
Okay, let's get started.
First, install the CLI onto your machine.
MacOS (using brew):
brew install infisical/get-cli/infisical
Windows (using scoop):
scoop bucket add org https://github.com/Infisical/scoop-infisical.git
scoop install infisical
Arch Linux (using yay):
yay -S infisical-bin
Check out the documentation for more installation options like Redhat/CentOS/Amazon and Debian/Ubuntu if your system is missing above.
Next, authenticate with Infisical using the CLI:
This command authenticates the CLI with Infisical Cloud or your self-hosted instance and stores credentials in your system keyring.
Next, navigate to the root of your project and run the initialization command:
This command creates a
infisical.json file holding a reference to your project in Infisical Cloud or self-hosted instance; the file can be committed to source control.
Finally, start your application in local development with the help of the CLI:
infisical run -- <your application start command>
If you're using a self-hosted instance of Infisical, you can run this command instead:
infisical --domain="https://your-self-hosted-infisical.com/api" run -- <your application start command>
This command fetches environment variables back from the development environment of your project in Infisical and starts up your application with the variables injected. The environment variables become available in the application environment. For example, they're accessible on
process.env in Node.js or on
os.environ in Python.
While the CLI comes with a few helpful subcommands and flags that you can read more about in the documentation, in practice, the basic command above is all you need 99% of the time.
Here're some examples for common frameworks:
# example with node infisical run -- node index.js # example with node (nodemon) infisical run -- nodemon index.js # example with Next.js infisical run -- npm run dev # example with flask infisical run -- flask run # example with django infisical run -- python manage.py runserver # example with laravel infisical run -- php artisan serve # example with rails infisical run -- bin/rails server # example with .NET infisical run -- dotnet run
Now you can use environment variables in local development without a .env file and feel confident that your team will not leak anything to source control. You're now also able to view all the environment variables for your application from one central place and avoid any missing environment variables.
I'm including this section because it has been helpful to developers in past articles.
Adopting a secret manager can be daunting at first and rightfully so. You have to trust that the solution you're working with is secure, that the vendor won't view or tamper with your data, and that the underlying infrastructure will work and deliver environment variables reliably when needed. There is, however, a right tool for every task and using dedicated tooling to manage environment variables is surely better than any manual process that's either not efficient or prone to error.
The analogy I like to use is password managers like 1Password and Bitwarden. Before using one, I was content with managing dozens of passwords manually and occasionally forgetting them. The minute I started to face password sprawl, however, I was compelled to upgrade my workflow and sign up for a password manager.
Similarly, in the realm of secret managers, when you're a solo developer, you can use manual approaches to store your environment variables. The minute, however, you start to scale to above five engineers, you start to face secret sprawl and that's where a secret management solution can really help. My suggestions for selecting one:
Pick a solution that's either open source or made by the big cloud providers; it will be more resilient that way and you can inspect and, in the open source case, make changes to the code if needed.
Pick something that provides a good developer experience for the size of your team. If you have site reliability engineers and teams for process, you can afford to go with more complex tools. Otherwise, if you're a smaller team, go frictionless and focus on building the rest of your application.
We developers often debate the practicality of using a secret management platform when faced with the need to yet manage another token to access environment variables or secrets. This unfortunately comes off as counterintuitive to many developers who then hesitate on adopting a secret management solution.
In this article, however, I demonstrate a workflow using a CLI that fetches environment variables and injects them into any application upon starting up; the method avoids storing any proxy token in a .env file. With this CLI approach, development teams stop risking leaking environment variables to source control by not storing any variables in plaintext in .env files on their machines.