Skip to main content

Environment Switching

Managing multiple environments — local, staging, production — across many repositories is painful without a consistent tool. This example shows how to define environments once and switch them all with a single command.

Scenario

A platform has two services. Each service needs different variables per environment, and switching to staging or production requires additional steps like confirming intent.

Profile

Profile-level environments define shared variables and tasks that apply across all repos. Repo-specific variables and tasks are defined in each repo's own raid.yaml.

platform-profile.yaml
name: platform

repositories:
- name: api
url: [email protected]:my-org/api.git
path: ~/dev/api
- name: frontend
url: [email protected]:my-org/frontend.git
path: ~/dev/frontend

environments:
- name: local
tasks:
- type: Print
message: "Switched to local"
- name: staging
tasks:
- type: Print
message: "Switched to staging"
- name: production
tasks:
- type: Confirm
message: "Switch ALL services to production?"

Per-repo environment config

Each repo defines its own environment variables and additional tasks in its raid.yaml:

~/dev/api/raid.yaml
environments:
- name: local
variables:
- name: DATABASE_URL
value: postgres://localhost:5432/api_dev
- name: API_HOST
value: localhost
- name: API_PORT
value: "3000"
- name: LOG_LEVEL
value: debug
tasks:
- type: Shell
cmd: docker-compose up -d db
- name: staging
variables:
- name: DATABASE_URL
value: postgres://staging-db.internal:5432/api
- name: API_HOST
value: api.staging.my-org.com
- name: API_PORT
value: "443"
- name: LOG_LEVEL
value: info
- name: production
variables:
- name: DATABASE_URL
value: postgres://prod-db.internal:5432/api
- name: API_HOST
value: api.my-org.com
- name: API_PORT
value: "443"
- name: LOG_LEVEL
value: warn
tasks:
- type: Confirm
message: "Point API at production database?"
- type: Shell
cmd: ./scripts/rotate-api-key.sh
~/dev/frontend/raid.yaml
environments:
- name: local
variables:
- name: VITE_API_URL
value: http://localhost:3000
- name: VITE_ENV
value: local
- name: staging
variables:
- name: VITE_API_URL
value: https://api.staging.my-org.com
- name: VITE_ENV
value: staging
- name: production
variables:
- name: VITE_API_URL
value: https://api.my-org.com
- name: VITE_ENV
value: production
tasks:
- type: Confirm
message: "Point frontend at production APIs?"

Usage

# Switch everything to staging
raid env staging

# Check which environment is active
raid env

# List available environments
raid env list

What raid env staging does

Applying environment 'staging'...
Switched to staging

Running repo environment tasks...
✓ api applied 4 variables
✓ frontend applied 2 variables

Done.

For production, the Confirm tasks gate the switch:

Applying environment 'production'...
? Switch ALL services to production? (y/N) y

Running repo environment tasks...
? Point API at production database? (y/N) y
✓ [api] rotate-api-key.sh
? Point frontend at production APIs? (y/N) y

Done.

Dynamic local environments

If local values differ between developers (ports, paths, credentials), use Prompt and Template tasks instead of hardcoded variables to generate values at apply-time:

~/dev/api/raid.yaml
environments:
- name: local
tasks:
- type: Prompt
var: DB_PORT
message: "Local database port"
default: "5432"
- type: Prompt
var: API_PORT
message: "Local API port"
default: "3000"
- type: Template
src: ./envs/local.env.tmpl
dest: ~/dev/api/.env
- type: Shell
cmd: docker-compose up -d db
~/dev/api/envs/local.env.tmpl
DATABASE_URL=postgres://localhost:$DB_PORT/api_dev
API_PORT=$API_PORT
LOG_LEVEL=debug

When a developer runs raid env local, they are prompted for their preferred ports and the .env is generated from the template. The committed template is shared; the generated .env stays out of source control.