Skip to content

/flags/bun/bun-env-file

bun --env-file: choose which .env file Bun loads

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
tool
Bun
command
bun
flag
--env-file=FILE
default
off; Bun loads .env, .env.$NODE_ENV and .env.local automatically

Bun reads .env files automatically: without any flag, bun index.ts loads .env, .env.$NODE_ENV (.env.development by default) and .env.local from the current directory into process.env and Bun.env. bun --env-file=.env.staging index.ts loads the named file instead, which is how you pick the configuration for one environment. Variables already set in the shell win over every file.

Automatic loading and --env-file

bash
cat > .env <<'EOF'
PORT=8080
NAME=default
EOF
cat > .env.staging <<'EOF'
PORT=7000
EOF
cat > main.ts <<'EOF'
console.log("PORT =", process.env.PORT, "| NAME =", process.env.NAME);
EOF
echo "--- bun main.ts (automatic .env)"
npx --yes bun@1 main.ts
echo "--- bun --env-file=.env.staging main.ts"
npx --yes bun@1 --env-file=.env.staging main.ts
echo "--- shell variable wins"
PORT=9000 npx --yes bun@1 --env-file=.env.staging main.ts

Output

--- bun main.ts (automatic .env)
PORT = 8080 | NAME = default
--- bun --env-file=.env.staging main.ts
PORT = 7000 | NAME = undefined
--- shell variable wins
PORT = 9000 | NAME = undefined

NAME exists only in .env. It is set in the first run and undefined with --env-file=.env.staging: the named file replaces the automatic list instead of adding to it. The shell value of PORT beats the file.

Load order without the flag

FileWhen it is read
.envAlways
.env.production / .env.development / .env.testDepending on NODE_ENV (development when unset; bun test uses test)
.env.localAlways, except when NODE_ENV=test
--env-file=FILEReplaces the automatic list with the given file(s); repeatable
Shell environmentAlways wins over any file

Later files override earlier ones, so .env.local can hold developer-specific values on top of the committed .env.

Syntax extras

Bun's parser supports variable expansion, which Node.js does not:

bash
# .env
HOST=db.local
DATABASE_URL=postgres://${HOST}/app   # becomes postgres://db.local/app in Bun
LITERAL=\$NOT_EXPANDED                # a backslash keeps the dollar sign

Quotes around values are removed, and # starts a comment on unquoted lines. Every value is a string.

Pitfalls

  • Because loading is automatic, a forgotten .env in the working directory changes behaviour without any flag on the command line. Check process.env when a value looks wrong.
  • The flag goes before the script or file: bun main.ts --env-file=x passes the text to your program.
  • bun build does not inline these variables unless you configure env in the build options; runtime and bundling are separate.
  • Keep credentials in .env.local (git-ignored) and commit only placeholder values.

Node.js and Deno have explicit flags with similar rules: node --env-file and deno run --env-file.

FAQ

Does Bun load .env automatically?

Yes. Without any flag, Bun reads .env, .env.$NODE_ENV and .env.local from the current directory. That is the main difference from Node.js, which needs --env-file.

Can I use --env-file with bun run scripts?

Yes. bun --env-file=.env.staging run start loads the file before the script runs, and the variables are visible to the script and to the processes it starts.

Do variables set in the shell override the .env file?

Yes. A variable that is already in the environment keeps its value; files only add variables that are missing.

Sources