DOC_INDEX
THEME
DOCS/Learn OrchStep/Working Directories

Working Directories

How OrchStep decides where commands run, the dir: field, and running a workflow from a different directory than its orchstep.yml.

Working Directories

When OrchStep runs a shell command, it runs it somewhere. This guide explains exactly where, how to change it with dir:, and how to run a workflow whose orchstep.yml lives somewhere other than your current shell directory.

The two directories that matter

  1. Your shell's current directory (cwd) — where you are when you type orchstep ….
  2. The workflow directory — the folder that contains the orchstep.yml being run. This is the one that matters for command execution.

The single rule to remember:

By default, commands run in the workflow directory — not your shell's cwd — and relative dir: paths resolve against the workflow directory.

This is deliberate: it makes a workflow behave the same no matter where you launch it from, which is what you want in scripts, CI, and shared repos.

The default: the workflow directory

With no dir:, every shell command runs in the folder holding orchstep.yml:

# ~/myrepo/orchstep.yml
tasks:
  build:
    steps:
      - name: where
        func: shell
        do: pwd          # prints ~/myrepo  (the workflow directory)

dir: — choose the directory

dir: is valid at workflow, task, and step level.

# ~/myrepo/orchstep.yml
tasks:
  test:
    dir: backend                 # every step in this task runs in ~/myrepo/backend
    steps:
      - name: deps
        func: shell
        do: go mod download      # ~/myrepo/backend
      - name: api
        func: shell
        dir: backend/api         # this step overrides the task -> ~/myrepo/backend/api
        do: go test ./...

Precedence (most specific wins):

step dir:  >  task dir:  >  workflow dir:  >  the workflow directory (default)

A step's dir: replaces the task's dir: — it does not nest under it. Both resolve from the workflow directory, so write the full sub-path on the step (backend/api, not api).

Path rules:

You writeResolves to
dir: backend (relative)<workflow dir>/backend
dir: ../siblingone level up from the workflow dir
dir: /opt/app (absolute)/opt/app as-is
dir: ~/workyour home directory + /work
dir: "{{ vars.service }}"template-resolved first, then the above rules

If the directory doesn't exist, the step fails fast and tells you the resolved path:

task 'test', step 'api': working directory 'backend/api' not found (resolved to /…/backend/api)
  Check the dir: value — relative paths resolve against the workflow file's directory.

Running from a different directory than orchstep.yml

You have three ways to run a workflow. The first is simplest; the others let you run from anywhere.

1. cd into the project (simplest)

cd ~/myrepo
orchstep run test

Your cwd equals the workflow directory, so everything is intuitive.

2. Point at the file with -f / --file (run from anywhere)

# from literally any directory:
orchstep run test -f ~/myrepo/orchstep.yml

The workflow directory is taken from the file's location (~/myrepo), not your cwd. So dir: backend still resolves to ~/myrepo/backend, even though you ran the command from /tmp or your home directory. Your cwd does not leak into where commands run.

Use an absolute -f path for full independence. A relative -f (e.g. -f sub/orchstep.yml) is resolved against your cwd, so the workflow directory becomes <cwd>/sub. That's fine — just know that relative -f is the one case where your cwd participates.

3. A shell alias or wrapper

For a workflow you run often from elsewhere, alias the file path:

alias deploy='orchstep run -f ~/myrepo/deploy.orchstep.yml'
deploy production

What about other path flags?

CLI path flags like --file and --vars-file are resolved relative to your shell cwd (they're things you type at the prompt). dir: is different — it governs where the workflow's commands execute, anchored to the workflow file. Keep the distinction in mind: flags = cwd-relative, dir: = workflow-relative.

Best practices by use case

Monorepo — one workflow at the root

Put orchstep.yml at the repo root and give each task the service it drives:

# ~/repo/orchstep.yml
tasks:
  web:    { dir: services/web,   steps: [ { name: build, func: shell, do: npm run build } ] }
  api:    { dir: services/api,   steps: [ { name: test,  func: shell, do: go test ./... } ] }
  infra:  { dir: infra/terraform, steps: [ { name: plan,  func: shell, do: terraform plan } ] }

Run any of them from anywhere with -f ~/repo/orchstep.yml, or cd ~/repo first. No cd clutter inside do:.

Umbrella workflow over sibling repos

A workflow that orchestrates neighboring checkouts (like an ops dashboard) uses ../ paths relative to where the umbrella orchstep.yml sits:

# ~/code/orchstep.yml   (siblings: ~/code/frontend, ~/code/backend)
tasks:
  build-all:
    steps:
      - { name: fe, func: shell, dir: ../code/frontend, do: npm run build }
      - { name: be, func: shell, dir: ../code/backend,  do: go build ./... }

Prefer relative paths (../code/backend) over absolute (/Users/you/code/backend) so the workflow is portable across machines and teammates.

CI pipelines

The workflow file is the anchor, so CI is predictable regardless of the runner's starting directory:

orchstep run deploy -f "$GITHUB_WORKSPACE/orchstep.yml"

When you genuinely want the caller's directory

Relative dir: is workflow-relative by design. If a task should act on wherever the user is, pass it in explicitly rather than relying on cwd:

orchstep run lint --var target="$PWD"
tasks:
  lint:
    steps:
      - { name: run, func: shell, dir: "{{ vars.target }}", do: golangci-lint run }

Rules of thumb

  • Keep dir: relative for portability; reserve absolute paths for true system locations (/opt, /var).
  • Set dir: at the task level when all of a task's steps share a directory; override on a single step only when it differs.
  • Don't cd inside do: — use dir:. It's clearer, it's validated, and the inspector/plan show the real directory.
  • Run with an absolute -f from scripts and CI for cwd-independence.

See also