Environment variables¶
Two fields, because they solve different problems. env_file is shared with the
team — it lives in the repository, or in .gitignore. env is the targeted
edit: right now I need debug logging on this one profile.
[profiles.api]
adapter = "dlv"
program = "cmd/api"
env_file = ".env"
env = { LOG_LEVEL = "debug", ROOT = "${root}" }
Precedence¶
From weakest to strongest: inherited environment → env_file → env.
What the profile states explicitly always wins. Otherwise a DATABASE_URL you
exported in your shell months ago would quietly beat the config, and nothing on
screen would say so.
A missing env_file is an error¶
Naming a file that does not exist stops the launch. It is not skipped silently: debugging with half the environment missing, and no trace of why, is worse than a launch that does not happen.
No automatic pickup¶
A .env sitting next to your code is not loaded on its own. Implicit
environment substitution is a surprise that gets expensive to debug later, and
the line env_file = ".env" takes the same amount of space while telling the
truth.
The .env format¶
A deliberately narrow subset is supported:
# a comment
DATABASE_URL=postgres://localhost/app
export PORT=8080
GREETING="hello world" # quotes preserve spaces
MULTI="a\nb" # inside double quotes: \n \r \t \\ \"
LITERAL='a\nb' # inside single quotes the value is literal
HASH=a#b # a comment only starts at ` #`
EMPTY=
There is no ${VAR} interpolation and there are no multi-line values. .env
has no standard; every library has its own substitution semantics, and the
mismatch "it interpolated for us but not in production" costs more to debug than
an explicit error. A line that does not parse stops the launch and names its
number.
With a repeated key, the last occurrence wins — as in a shell.
Secrets stay out of the logs¶
Values of env never appear in the log or in the Logs panel: DAP request bodies
are not printed. This is an invariant, not a side effect — request tracing cannot
be extended without stripping env values first.
That is also why the "long request" message in the log includes only mode and
program from the request body.
PHP is the exception¶
env and env_file do not apply to PHP. The adapter launches nothing — it
listens on a DBGp port while the PHP process is started by PHP-FPM, Docker or a
CLI invocation. There is nobody for Bugsaur to hand an environment to.
Set the environment where PHP actually starts: docker-compose.yml, the FPM pool
config, the container's own .env. See Languages → PHP.
Common cases¶
The application does not read .env itself¶
If the variables used to arrive from direnv or an editor extension rather than
from something like godotenv.Load(), the working directory will not help — you
need the file named explicitly:
A monorepo with a shared .env above the root¶
# services/api/.bugsaur/config.toml
[profiles.api]
adapter = "dlv"
program = "."
env_file = "../../.env"
Variables that cargo run sets and a direct launch does not¶
See Languages → Rust.