How to Switch Environments with raid env

by Alex Salerno

A team that ships against local, staging, and production ends up doing the same dance every time someone needs to switch: edit five .env files, run different startup commands per repo, hope nothing's stale. raid env <name> collapses that into one command.

This guide covers how raid env works, where variables come from, and the merge rules between profile-level and repo-level definitions.

1. The shape of an environment

Environments are declared at the profile level. Each has a name, optional variables, and optional tasks:

# web-platform.raid.yaml
name: web-platform
repositories:
  - { name: frontend, path: ~/Developer/frontend, url: https://github.com/acme/frontend.git }
  - { name: backend,  path: ~/Developer/backend,  url: https://github.com/acme/backend.git }

environments:
  - name: local
    variables:
      - { name: API_URL,  value: http://localhost:8080 }
      - { name: NODE_ENV, value: development }
  - name: staging
    variables:
      - { name: API_URL,  value: https://api.staging.acme.com }
      - { name: NODE_ENV, value: production }
    tasks:
      - type: Shell
        cmd: echo "Pointing at staging."

2. Switching environments

raid env staging

What happens, in order:

  1. Raid records staging as the active environment in ~/.raid/.
  2. For every repository in the profile, Raid writes a .env file at the repo root containing the merged variables.
  3. Raid executes the environment's tasks: sequence.

Every repo now has the same .env content, and every subsequent raid <command> runs against staging.

3. Showing and listing

raid env            # show the currently active environment
raid env list       # list every available environment

Both support --json for scriptable output — see How to wire Raid into CI.

4. Per-repo overrides

The profile defines the shape of the environment (which envs exist, what's globally true). Each repo can layer overrides on top in its own raid.yaml:

# inside frontend's raid.yaml
environments:
  - name: local
    variables:
      - { name: PORT, value: '3000' }

When raid env local runs, the frontend repo's .env file gets the profile's variables plus PORT=3000.

If both define the same key, the repo's value wins for that key — handy for ports, paths, or other repo-shaped values.

5. The merge rules in one place

For a given environment name, Raid loads the profile entry first and then the repo entry. The merge is conservative and predictable:

FieldProfile + Repo behavior
variablesProfile vars first, then repo vars. Repo entries override matching keys per-key.
tasksProfile tasks first, then repo tasks. Both run, in that order.
nameMust match exactly. Repos can't introduce new env names — only profile can.

The practical effect: profile-level changes propagate to every repo; per-repo overrides are localized. You can read any single repo's raid.yaml and know which values it adds on top.

6. Common patterns

Switching the API endpoint everyone hits

The classic case — one variable that needs to change across the whole stack:

environments:
  - name: local
    variables: [{ name: API_URL, value: http://localhost:8080 }]
  - name: staging
    variables: [{ name: API_URL, value: https://api.staging.acme.com }]
  - name: production
    variables: [{ name: API_URL, value: https://api.acme.com }]

raid env staging and every repo's .env file points at staging.

Per-repo ports

Each microservice listens on a different local port. Define them in each repo's raid.yaml:

# auth-service/raid.yaml
environments:
  - name: local
    variables: [{ name: PORT, value: '8081' }]

The profile's local env gives every repo API_URL; each repo's local env adds PORT on top.

Running setup tasks on switch

Sometimes switching environments isn't just variables — you also need to restart a Docker network, prime a cache, or print a banner:

environments:
  - name: production
    variables: [{ name: API_URL, value: https://api.acme.com }]
    tasks:
      - type: Print
        message: ⚠️  You are pointed at PRODUCTION.
        color: yellow
      - type: Confirm
        message: Continue?

Tasks run after the .env files are written. Combine with Confirm tasks for safety rails.

7. Reading the active env from your app

Each repo's .env file is plain KEY=VALUE. Use whatever your stack already uses — dotenv in Node, direnv shell loading, Go's os.Getenv after godotenv.Load(). Raid manages the file's contents; your app reads it normally.

8. Resetting / clearing

There's no explicit "no environment" mode — you switch by running a different raid env <name>. To re-emit the current env's .env files (e.g. after editing the profile), just re-run the same command:

raid env local   # rewrites every .env to match the profile + repo definition

Next steps

More articles

How to Add a Health Check to a Raid Workflow

Use the Raid `Wait` task to block on HTTP endpoints or TCP ports until a service is healthy — and pair it with `Group` for retry semantics on flaky deps.

Read more

How to Add a raid.yaml to an Existing Repo

Commit a raid.yaml to any repo so the Raid CLI can run its commands, environments, and install steps — and merge them with the team profile automatically.

Read more