5 ms·
Where .env Went Wrong
- jonny_eh 2mo agoAlso: https://varlock.dev/ https://varlock.dev/
- qntty 2mo agoI use Pydantic Settings for this, would be nice to see a comparison to that. I do like the idea of keeping this in a configuration file.
- triyambakam 2mo agoYeah Pydantic Settings is pretty nice
- domenkozar 2mo agoTwo things: - Secrets don't belong in config https://secretspec.dev/blog/secrets-dont-belong-in-config/ https://secretspec.dev/blog/secrets-dont-belong-in-config/ - You want to have flexibility of choosing between any secrets provider: https://secretspec.dev/blog/but-i-use-sops/ https://secretspec.dev/blog/but-i-use-sops/
- tomjakubowski 2mo agopydantic-settings is not limited to loading from config files. It is easy to populate a settings model from environment variables, for example. With a little bit of glue code it could load from the secretspec sdk.
- eigencoder 2mo agoBut secrets are a kind of configuration, right? I agree they should be kept separate from non-secret config; ideally non-secret configuration can be committed to your codebase somewhere depending on the environment it's running in.
- Natfan 2mo agoClaude, find a key underpinning of most development workflows, then make a product to disrupt (and eventually SaaSify/enshitify) it. Finally, write a convincing ad disguised blog post, and purchase a fancy domain. Here's my credit card.
- theozero 2mo agoWhile there are absolutely a million of these env tools popping up which are total vibe-coded slop, secretspec is not one of them. It's from the creator of https://devenv.sh https://devenv.sh and has been around for a while.
- domenkozar 2mo ago<3
- Sebguer 2mo agothe blog post is absolute slop though
- domenkozar 2mo agoDon't take the black pill <3
- deleted 2mo ago[deleted]
- theozero 2mo agoOver at varlock (https://varlock.dev https://varlock.dev -- also free, open source), we agree that .env as we know it is full of problems. But instead of abandoning it, we evolved it. We replace your .env.example with a .env.schema - using decorator style comments to add schema info, and functions to load and compose values. A big difference between our tool and many other similar tools is that we combine the schema and value setting into one surface, with a way of merging many definitions together, much like cuelang - but in a way that feels more intuitive. It's extremely flexible, and can even do credential brokering for untrusted workloads. I've been enjoying the secretspec content lately, and watching it evolve :)
- cyanregiment 2mo agoIt's too bad they call background services "agents" now instead of daemons like they used to, because your slogan could be Secrets for daemons Varlock is clever. Sub string types for .envs pretty cool, why not
- c-hendricks 2mo agoAlso mise: https://mise.jdx.dev/environments/ https://mise.jdx.dev/environments/
- kstrauser 2mo agoSeconded. I've also had great luck with the same author's fnox secret manager, especially because you can tell it to fetch some secrets from the user's laptop's OS keychain, and some out of 1Password, and some out of AWS KMS, and some out of [probably your own favorite provider (https://fnox.jdx.dev/providers/overview https://fnox.jdx.dev/providers/overview)].
- domenkozar 2mo agofnox is mostly a copy of secretspec (and it's good to have one!), so you'll find the same kind of support in both. I wish instead we'd collaborate with Jeff, but oh well. The difference is that we're providing an interface for applications to build with, with 8 SDKs available so you can have first-class support for secrets whatever you're bulding. I do hope fnox copies that too!
- kstrauser 2mo agoYou keep saying that but I disagree strongly. Having looked at both fnox and secretspec, there are some obvious similarities because they're both in the same space, interacting with many of the same tools, and there are only so many ways to declaratively state "set the FOO variable with the 'my_secret_key' value out of the Bar provider". Those inherent similarities aside, I don't see it. They don't seem to have a lot in common as far as design or implementation or configuration.
- Kydlaw 2mo agoThat's what we are using as well in our small team. `mise` for our non-sensitive values/ environment variables and then `fnox` (from the same author) to encrypt and manage the secrets. `mise` has a plugin for `fnox` that allows to automatically load the secrets in the context when running a mise task/command. This way, our secrets are never exposed, even during local development. Setting this up was effortless. Pretty happy about these tools.
- tosti 2mo agoOne could also... Not jump on the bandwagon? (Am I getting thrown out the window now? :)
- hagbard_c 2mo agoThe Doppler effect on your last words seems to confirm you just did.
- ctippett 2mo agoI'm sure there are many better alternatives to .env, but its ubiquity and support across various tools makes it super convenient. I'm using 1Password's .env integration[1] and although the UX is a bit clunky, I really like it. My API keys are secure, tools that ordinarily support .env just work and there's a team-sharing feature too (although I'm yet to use it). It's pretty neat. [1] https://www.1password.dev/environments https://www.1password.dev/environments
- eigencoder 2mo agoHm, I haven't seen these issues personally. We only have one `.env` file and it's just for local secrets. Configuration emphatically does not go in `.env` and ideally is in docker compose and defined in code (we use Pydantic Settings).
- tp3358 2mo agoYeah, this is the way. I assumed that was fairly standard at this point.
- domenkozar 2mo agohttps://x.com/NoeticsTech/status/2071136897985138897 https://x.com/NoeticsTech/status/2071136897985138897 https://x.com/0xPrajwal_/status/2084163731534311627 https://x.com/0xPrajwal_/status/2084163731534311627 https://x.com/nilbuild/status/2084339074639200488 https://x.com/nilbuild/status/2084339074639200488 The issue is that agents need structure and context.
- preommr 2mo agoNo it isn't. Your local .env should NOT be shared, but should also be assumed to be leaked at any given time. Security should be done using a secrets manager through the cloud platform that's being used, e.g. AWS'secrets manager (or ssm param store too i guess) Pasting it into chatgpt should not be a problem. It also doesn't belong in git repos, but a much bigger issue is what process led to it. It's pretty standard to ignore it in a .gitignore, I'd be very surprised if modern agents made that mistake. And even if they did, agents should call tools that scaffold deterministically so that this isn't a problem. I genuinely don't even know what we're talking about anymore, .env files are just values (it even says so in the article). People are making it way more complicated than it needs to be for no discernable reason other than an insane amount of laziness.
- KevinMS 2mo ago> Where .env Went Wrong start with making it a dot file. Why would you want to hide the fact your app was using loaded environment variables?
- jerf 2mo agoI hate that it's hidden too. There's no reason for it to be hidden and arguably some reason for it not to be hidden. I did discover one reason for it not to be just "env" though, which surprised me, which is that "source env" will yield "bash: source: /usr/bin/env: cannot execute binary file". I did not expect the source command to use the $PATH to resolve the filename. Probably some minor security issues that can result from that out there in the world. Arguably anyone loading it should use "source ./.env" or equivalent, with full path. It's documented in the bash manual, of course. But it is rather complicated as to when it will and will not use $PATH.
- ciupicri 2mo agoFrom the Bash manual [1]: . [-p path] filename [arguments] > If filename does not contain a slash, . searches for it. If -p is supplied, . treats path as a colon-separated list of directories in which to find filename; otherwise, . uses the directories in PATH to find filename. filename does not need to be executable. When Bash is not in POSIX mode, it searches the current directory if filename is not found in $PATH, but does not search the current directory if -p is supplied. If the sourcepath option (see The Shopt Builtin) is turned off, . does not search PATH. From "The Open Group Base Specifications Issue 8 - IEEE Std 1003.1-2024" [2]: . file > If file does not contain a <slash>, the shell shall use the search path specified by PATH to find the directory containing file. Unlike normal command search, however, the file searched for by the dot utility need not be executable. So it seems that it's not something Bash specific. [1]: https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#index-_002e https://www.gnu.org/software/bash/manual/html_node/Bourne-Sh... ) [2]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_19 https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V...
- s_dev 2mo agoThe fact that it's hidden means it doesn't get commited by accident in most git repos unless explicitly added or configured that way.
- Brainspackle 2mo agoYou can easily use comments in the file for guidance, eliminating half your complaints
- saadyousfi 2mo ago[dead]
- daniel_mcsoft 2mo ago[flagged]
- nirbendavid 2mo ago[flagged]
- chis 2mo ago92% of this text is detected as AI. It may be time for Hackernews to integrate a Pangram detector into the UI, similar to what substack is doing :)
- devmor 2mo agoI’m not saying whether I think you’re right or wrong, but AI text detectors don’t work and never will.
- richwater 2mo agoDoesn't stop charlatans from creating startups promising to make it work :)
- chis 2mo agoThis has been pretty well studied. Pangram is about as good at an expert human with a 98% detection rate and <2% false positive, for flagging AI generated text. https://arxiv.org/pdf/2501.15654 https://arxiv.org/pdf/2501.15654
- devmor 2mo agoI'm open to being convinced, but the study you've linked me is limited specifically to professionally written, proofread journalism from 8 publications over a span of a little over a single year. It's not exactly a wide breadth of varied text for such an all-encompassing task.
- chis 2mo agohttps://www.pangram.com/blog/third-party-pangram-evals https://www.pangram.com/blog/third-party-pangram-evals You can also just try it yourself I guess really what convinced me was how it perfectly agrees with my own judgement.
- devmor 2mo agoThe primary reason I am not convinced is because it (and several other tools) frequently classify my own writing as AI-generated. Especially my technical writeups. Now, I could just assume I'm an outlier within that small false-positive rate. But I find it far more likely that the efficacy of these tools is much lower than advertised and they rely on confirmation bias.
- prologic 2mo agoThis is why I love direnv (the tool) and just a simple .envrc (not .env). It has none of the problems posited in the OP's article. It assumes nothing, only that direnv manges loading up the .envrc into your shell.
- LeBit 2mo agoI moved from direnv, asdf and Task combo to mise. Mise env vars management is much better. If you really need a script to generate env vars (like what you can do with direnv, there is a clean mechanism for that). If you need to do something when you enter the directory of a project, there is also a mechanism for that. Mise is really nice.
- chacham15 2mo agoThis article reads like more of an ad than anything else. > Environment variables only deliver values Yes, the problem .env files try to solve is having "environment variables" be injectable from a file so that different applications can have different environment variables by default. > A string is not a schema Yes, input validation is an application concern. The application should know what these values represent / how to parse them and error if they're invalid. I could go on, but the article is all about trying to use a hammer as a screwdriver and complaining that the hammer doesnt work.
- jt2190 2mo ago> Yes, the problem .env files try to solve is having "environment variables" be injectable from a file so that different applications can have different environment variables by default. This seems like a misunderstanding: The `.env` file should be `source`d into the current shell (environment). The application reads values using whatever mechanism it uses to read these values from the environment. Nothing should be “injected” into the application, i.e. the application should not read `.env` directly. (Not sure if you were just being loose with your terminology, trying to clarify.)
- throw-the-towel 2mo agoDoes it matter whether the app reads `.env` directly though?
- e12e 2mo agoMaybe. How does the application unify different environment variables? Those inherited by the shell, those read from .profile, those set in the process that start the application (be that ./run.sh, a nodejs script, systemd etc...).
- throw-the-towel 2mo agoWhy would it want to? I'd say what you're asking for is actually a code smell. An app should not have a bazillion interlocking ways to be configured, unless there's a strong reason for this!
- weinzierl 2mo agoI see many .env files with unquoted values, like the first example in OP REDIS_URL=redis://localhost:6379 and it triggers me big time. Always quoting shell variable values is so deeply ingrained from very painful experiences decades ago that it still causes a somatic flashback whenever I see it. This is what went wrong with .env Yes, I know docker --env-file doesn't work with quotes but that's just docker being broken and why they fixed it in docker compose. Also see jt2290's comment: https://news.ycombinator.com/item?id=49171684 https://news.ycombinator.com/item?id=49171684
- deleted 2mo ago[deleted]
- TZubiri 2mo ago.env files are fine, as long as you don't import a random dependency to open and read a file. If you think about it for ten seconds, it's unjustifiable, and it's only done because others do it.
- deleted 2mo ago[deleted]
- stilwe 2mo agoHey cool, a relevant place to mention my project: https://dotprot.dev/ https://dotprot.dev/ Type "dotprot" to copy your .env file into 1Password using the 1P CLI, it verifies it's there, then deletes the local copy of .env. When you're ready to work again, type "dotprot" again to restore the .env file from 1P. There is a .prot file in the directory that you can declare other files to store in 1P as well. Varlock (https://varlock.dev https://varlock.dev ) is also good for this and more mature. I use it on some work projects, but for local dev, I wanted something to allow me to be even lazier than that.
- jpitz 2mo agoIf 1Password hadn't jumped the subscription model shark, I'd be all over that. Any chance you'd entertain supporting bitwarden?
- theozero 2mo agoVarlock has bw plugin too - and similarly you can either wire up individual items or pull a whole env style blob from a single item if you prefer.
- theozero 2mo agoFYI - You can pull a whole .env style blob from a single item using varlock. Never written to disk and supports caching behind Secure Enclave.
- throw-the-towel 2mo agoI can't understand for the love of me why our industry settled on env as The Way to store configuration. It's poorly discoverable, it's a shared namespace polluted by everyone and their `libdog`, it's stringly typed, etc. All of that just flies in the face of otherwise accepted dev wisdom. I love Django's approach to configs: your config is just a plain old Python file with some constants. As simple as possible, and works great.
- e12e 2mo agoWorst part they're too easy to accidentally check in to source control - and they bring user/workstation dependent settings into the work tree, rather than tuck them away in something like $HOME/. config/appname/local.env
- wonger_ 2mo agoI'm working with .env and secrets and teamwide configs for the first time, trying to understand solutions from first principles. Are these valid observations so far? - .env is the simplest and oldest / most boring solution. Secrets are passed to teammates by DM? - .env.example seems like a nice home for documentation about secrets - but I'd rather avoid writing sensitive credentials at all -- instead, teams can use a cloud-based secrets manager and the project fetches secrets at runtime. Is this ever a hassle / any downsides? How standard is this practice these days? I hear this also helps updating secrets so you don't have to e.g. tell every teammate when you rotate a secret. Generally not a fan of introducing a network call and a saas dependency, tho - non-sensitive environment variables also need a home. I don't like the idea of cluttering the project with a .env.local or worse like .env.development.local - I hear lots of secrets and config management happens in container orchestration, but what if I barely have a container in the first place? I guess docker compose is one of the simplest tools at this layer? I guess I'm just looking for a safe, simple solution for a small team, and maybe the problem is that every team does things differently, and that many tools are marketed towards huge enterprise teams.
- theozero 2mo agoVarlock sounds like what you might be looking for. Free, open source, and very flexible toolkit to use however you like.
- worksonmine 2mo ago> - .env is the simplest and oldest / most boring solution. Secrets are passed to teammates by DM? I don't see a reason to ever pass them. Sensitive production values should never be accessible to everyone, and for development the team should be able to create them themselves. I usually keep very stupid values in .env.example, with the instructions to `cp .env.example .env`. In development it doesn't matter if the password is "password". In production the privileged admin sets up actual values. > - non-sensitive environment variables also need a home. I don't like the idea of cluttering the project with a .env.local or worse like .env.development.local `cp .env.example .env` and the application uses .env just like in production, only with dummy values.
- gaigalas 2mo agoLikely a 12 factor legacy issue. https://12factor.net/ https://12factor.net/ This was really popular, and lots of people bought the factor III of only configuring via environment variables. The whole point of it was: keep config simple. Env vars are austere and will keep you from complexity. But people found a way to complicate them, so they could have both the 12 factor literal pass and the complexity they wanted. That resulted in the mess we are today.
- ramses0 2mo agoI wish there was better support for something like `SECRET_CMD="..."` (ie: "run this command to get/refresh secrets"), but that's just baking arbitrary command execution into your development pipeline. Personally, I tend to have a `source ./source-me-auth` which does stuff like `DB_PASSWORD="$( pass show blah.com | head -1 )"`, but that still means I end up with secrets dangling around in my environment. The "source-me" file is safe to share, but my runtime environment is tainted (for good and bad...). I saw something w.r.t. the way open-claw handles things where you basically say `export DB_PASSWORD="REDACTED@SECRET_001"` and then it basically outbound filters network requests to hydrate that with `s/REDACTED@SECRET_001/$REAL_SECRET/g`, and I _really_ like that mechanism b/c it defers secret usage to runtime and keeps it physically "out" of the application itself. Basically, having `with $SECRETS -- some_app.sh --some params ...` wouldn't be terrible (conceptually).
- 0xbadcafebee 2mo agoIt's interesting how different roles come up with different solutions based on their own experience. Developers came up with .env because they would run apps on their own machine and wanted to pass variables to their application without having to set them in a command-line or environment-variable every single time they ran their app. And it works great for that. If you're one developer, running something locally, sure, just read some lines from a file. And if you're a team of developers, and most of you have the same lines you want to use, but maybe just a few of them you want to change per developer machine, fine, either keep .env out of Git, or keep a .env.local for the non-Git stuff. Again, simple, works fine. Then you want to run your apps in production, with different lines. And then you want to run it on a test/stage/qa machine too - again, different lines. And maybe you hard-code those into separate files (.env.prod, .env.staging, .env.test) and load them depending on which of the 2 or 3 machines you have running your app. Again, simple, works fine. Until the problems start. Secrets in the .env? Now those are in the code, which gets cloned everywhere, and can be stolen. "Encrypted" secrets in .env? Now you have to manage a secret key out of the file, in addition to the encrypted secret in the file. Everyone has access to the secret? Now people can use those secrets, or access different machines, potentially creating problems or exceeding their authority, and there is no way to know who did what because it's one shared secret. Somebody leaves the company? Now you need to rotate the secret (which nobody does). Ephemeral containers/deployments? Now the .env entries don't match the new hostname. Want to support multiple hosts? One hostname in the .env line doesn't work anymore. Want to scale horizontally? Now your "prod/test/dev" files are more of an environment type than a specific host. Your RDS database's hostname, or an old IP address, has changed? App is broken, time to update all the .env lines referring to it and re-deploy the app. These are all problems that you might or might not run into. But they are problems that do exist in the world; we know they happen, and we know how to avoid them. You decide whether you're going to wait for them to bite you, or avoid them altogether from the very start. If you do the former, you're acting like a Systems Engineer, designing a system to be resistant to known failures. If you do the latter, you're not. You may not want to do extra work you feel is unnecessary just to avoid a possible problem. But other professions do this anyway, by regulation, because (for example) as a society we we don't want to allow houses to burn down from a preventable problem. Example: If you run wire in a conduit, the conduit must be a minimum size depending on the number and type of wires you run in that conduit. You may think it's annoying that you have plenty of space left in your conduit; why should I have to change my wire size or conduit size? But if the wires take too much current, and don't have enough airflow/space between them, they can heat up and start a fire, or a short, which could cause a larger problem somewhere else (like taking out a hospital's ventilators, as one example). Doing the extra "unnecessary" work prevents fires. That's why Systems Engineers don't use .env files. Their job is to build reliable systems, not just roll out a feature and cross their fingers.
- leftnode 2mo agoI suppose I'm spoiled by the Symfony framework which has a robust Secrets Component [0]. It allows you to generate a public/private key for each environment: test, dev, and prod (by default). The 'prod' private key can't be committed to the repository. Once the cryptographic keys are generated, you can encrypt any secret you want, and the framework will automatically decrypt it and allow you to access it as an environment variable at runtime. The encrypted values for all environments are committed to the repository. For example, the file for the `$STRIPE_SECRET_KEY` environment variable for the 'dev' environment is a file that has these contents (shortened for readability): <?php // dev.STRIPE_SECRET_KEY.c33678 return "\x1Dq\x11B6\x5B\xFE7\x9B\xA4\xFE..."; This solves several problems: 1. Keys for the 'dev' and 'test' environments can easily be shared with the team because their public and private keys values are committed to the repository. No more sharing secrets file over Slack or through some other mechanism. 2. The private key for the 'prod' environment can be stored in a 3rd party vault so it can be made available to the production servers. You can also decrypt 'prod' secrets during deployment to reduce the decryption overhead for each request. 3. At worst, agents would have access to the secrets in the 'test' and 'dev' environments only because they couldn't decrypt 'prod' values locally. 4. It forces good practices to ensure you're not using the same secret value for production and non-production environments. It's made secret management so easy I don't even give it a second thought. Do other web frameworks support a system like this? [0] https://symfony.com/doc/current/configuration/secrets.html https://symfony.com/doc/current/configuration/secrets.html
- ghshephard 2mo agoI'm guessing my brain has been permanently warped by working in a hashistack environment for the last 5+ years. All of these KV pairs are always either in a consul (bog-standard config information) or vault (secrets/credentials) in a consul-template associated with the job - and rendered at run time. And, of course - critically - the consul-template updates those credentials and values as they are routinely rotated. I did a ^F for "update" and "dynamic" - didn't find any hits in that doc. Managing your dynamic environment values is table-stakes in most large deployments - and I didn't see any reference in `secretspec` as to how they plan to do that.
- osinix 2mo agoHaving a crowded and sloppy environment, is not a good way to go. When you log in to your account, you should not source a bunch of environment files by default. It is always a good practice to have a simple environment, just whatever you needed be sourced. If you need extra, just source them before using them. Keep your home clean.
- vekker 2mo agoHm, the main headache I have with .env files is simply that I lose them (switching between machines etc...), or the trouble of sharing secrets with team mates, etc... The headache of keeping them up to date and synced across environments & machines. Not affiliated but those troubles are now finally solved with Infisical for me. It's open source so I happily give them a plug.
- deleted 2mo ago[deleted]