Lately I've seen a lot of projects use .env files to store application configuration, files which look something like this:
FOO=bar
Bar=baz
There are a lot of tools which consume these files, like foreman
and
dotenv
, so all seems good, the file contents are straight-forward, and
everyone is happy, right?
I think these files are a bad idea.
First of all, what are these files, really? Are they shell scripts? Am I allowed to do this:
FOO=bar
BAR=$FOO
I guess probably not. In fact they don't really make sense as shell script, since there's no real way to execute these files and load the results into a shell. It's possible to source them into a shell, but since they're not exported they won't affect any programs started from this shell, so this is essentially useless.
In fact most programs which consume these files implement a parser which parses them into a data structure. See for example foreman's implementation here.
So if they're not shell scripts, and everyone parses them into data structures, .env files are in fact a serialization format. And if they are a serialization format, there are a couple of questions we should ask us:
- Is it legal to quote values?
- What characters can I use for quoting?
- Are comments allowed in these files?
- What encoding are they in?
- What happens if I insert a null byte?
- Is there any way to escape a newline?
- Are windows-style line-breaks allowed?
- Can lines be separated by semi-colons as in bash?
- Can I put binary data in there?
And so on and so forth.
The problem is that .env files don't have a specification. Everyone who writes a parser for them essentially invents their own, probably slightly incompatible format.
We are left with two possible solutions:
- Use an actual shell script
- Use an actual serialization format
If you use the first option, be aware that you cannot use any of the tools
which consume .env files. foreman for example silently ignores all lines which
are prefixed with export
. dotenv actually does the right thing and allows a
leading export
, but again, the behaviour differs between implementations.
There's no common spec.
I think the second option is far better. There are a bunch of existing serialization formats which work just fine. You can use TOML, YAML, XML, JSON or whatever else you like.
Or someone could sit down and write a proper spec for the .env file format with a reference implementation. But really, why bother?
The reason
.env
files are nice is because there's a direct mapping from the format to environment variables, so the cognitive overhead is minimal. There are definitely problems with the format, but the simplicity is undeniable.