Homelab hours are real hours.

title: set -a for sourcing .env files
date: 2026-02-20 · 1 min read
type: til · tags: #linux #shell #learning

set -a for sourcing .env files

Why `source .env` does not always do what you think, and a one-liner that fixes it.

I had two .env files — one at the workspace level with shared credentials, one at the project level with project-specific secrets. I sourced both:

source ../.env && source .env

Then ran my CLI tool. It prompted me to log in interactively, as if the variables did not exist.

And they did not — at least not where it mattered.

The problem

source runs each line in your current shell. A line like CLOUDFLARE_API_TOKEN=abc123 sets a shell variable, not an environment variable. Shell variables are local. Child processes — anything you launch from that shell — never see them.

source .env
echo $MY_VAR        # works — same shell
my-cli-tool          # does not see MY_VAR — since it is a child process

The fix

set -a turns on allexport. Every variable assigned after that point gets automatically exported to the environment. set +a turns it back off.

set -a && source ../.env && source .env && set +a

Now my-cli-tool sees everything. The set +a at the end is housekeeping — anything you assign after that stays local to your shell, which is usually what you want.

Why it layers nicely

If both files define the same variable, the second source wins. This gives you a clean override pattern: shared defaults in the parent directory, project-specific overrides in the current one. Same as how most tools handle config inheritance.

The shortcut I always forget exists

You can also use export $(cat .env | xargs), but it breaks on comments, multi-line values, and anything with spaces. set -a handles all of those correctly because it lets the shell do the parsing.