Skip to main content
At sufficient scale, infrastructure is not one repository — it’s many. The networking stack in one repo, the platform (EKS, IAM) in another, the backend services in a third, the frontend in a fourth. Each team owns their repo, each stack has its own apply pipeline. But the stacks aren’t independent: the backend needs the platform to exist before it can deploy, the platform needs the networking to be in place, the frontend needs the backend API to be reachable. Running these in the wrong order produces failures. Running them manually in the right order doesn’t scale. The workspace block is ubx’s answer: a declarative manifest of your multi-repo dependency graph. Declare the repos, their stacks, and which stack depends on which, and ubx workspace apply runs them in topological order — parallel where safe, sequential where required.

What you’ll learn

  • How workspace declares a multi-repo stack dependency graph
  • How depends_on expresses cross-repo dependencies
  • How ubx workspace apply orchestrates the graph in topological order

Why this matters

workspace is documentation that runs. The dependency graph between your repositories is always implicit — workspace makes it explicit, machine-readable, and executable. A new team member can read the workspace manifest and immediately understand the full platform topology.

The source code

# workspace "name" declares a multi-repo orchestration manifest.
# No TypeScript/Python/Go is generated — this block is read by the
# workspace runner (ubx workspace plan/apply) only.
workspace "myorg" {
  repo "platform" {
    url    = "github.com/myorg/platform"
    stacks = ["networking", "eks"]
    # no depends_on — runs first, in parallel
  }

  repo "backend" {
    url    = "github.com/myorg/backend"
    stacks = ["api", "worker"]
    depends_on = {
      api    = ["platform/networking", "platform/eks"]
      worker = ["backend/api"]
    }
  }

  repo "frontend" {
    url    = "github.com/myorg/frontend"
    stacks = ["web"]
    depends_on = {
      web = ["backend/api"]
    }
  }
}

How it works

1

ubx builds a topological sort of the stack graph

The workspace manifest declares stacks as nodes and depends_on entries as directed edges. ubx builds a DAG and topologically sorts it: platform/networking and platform/eks run first (in parallel), then backend/api (depends on both), then backend/worker and frontend/web (in parallel, since both depend only on backend/api).
2

Parallel stacks run concurrently

Stacks with no dependency relationship between them run concurrently. platform/networking and platform/eks have no declared dependency on each other — ubx runs them in parallel. ubx workspace apply uses goroutines to execute independent stacks simultaneously, minimising total wall-clock time.
3

workspace produces no generated Pulumi code

workspace blocks compile to zero TypeScript/Python/Go. They’re read by the ubx workspace subcommand, which clones each repository, resolves the dependency graph, and runs ubx apply on each stack in order. The workspace runner is separate from the per-stack compiler.

The execution order

ubx workspace apply

  ├─ [parallel] platform/networking
  ├─ [parallel] platform/eks

  ├─ [after platform/networking + platform/eks] backend/api

  ├─ [parallel, after backend/api] backend/worker
  └─ [parallel, after backend/api] frontend/web
Total wall-clock time is dominated by the longest sequential chain — platform → backend/api → frontend/web — not the sum of all stacks.

Common mistakes

depends_on entries use the format "repo_name/stack_name" — for example "platform/networking", not "github.com/myorg/platform/networking" or just "networking". The repo name is the label in the repo "..." block, not the full GitHub URL. Using the wrong format produces a compile error.
You don’t need to declare a workspace block in every repository — only in the repository (or a dedicated orchestration repo) that owns the top-level orchestration manifest. Individual stack repos remain independent and can be applied directly with ubx apply without the workspace runner.

Run it

ubx validate                    # validates the workspace manifest syntax
ubx workspace plan              # plans all stacks in topological order
ubx workspace apply             # applies all stacks in topological order
ubx workspace apply --stack backend/api  # apply a single stack only

What you learned

workspace declares a multi-repo dependency graph — stacks with depends_on run after their prerequisites
Independent stacks run in parallel; dependent stacks run sequentially
workspace produces no generated code — it’s read by the ubx workspace subcommand only

Next steps

remote stack reference

Consume outputs between stacks in the workspace

workspace block reference

Full workspace block syntax and all options