Skip to content

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_fileenv.

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:

[profiles.api]
adapter = "dlv"
program = "cmd/api"
env_file = ".env"

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

env = { CARGO_MANIFEST_DIR = "${root}" }

See Languages → Rust.