> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ubiquex.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Stack Dependency Resolution

> ubx automatically resolves and plans/applies stack dependencies in the correct order using @name.output cross-stack references.

Splitting infrastructure into multiple stacks is the right call at scale — networking lives in one stack, the EKS cluster in another, applications in a third. The hard part, traditionally, is orchestration: which stack do you plan first? Which apply do you run before the next one can proceed? Terragrunt solves this with explicit `dependency` blocks in `terragrunt.hcl`. Pulumi solves it with `StackReference` objects you wire up manually. ubx solves it by reading your `.iac` source: the moment you write `@networking.vpc_id` in the cluster stack, ubx knows the networking stack must be planned and applied before the cluster stack can run. No orchestration file. No manual ordering. The dependency graph is derived from the references you already have to write anyway.

## What you'll learn

* How `@name.output` cross-stack references trigger automatic dependency resolution
* How ubx discovers sibling stacks by name within the same repository
* The plan output showing stack-boundary headers and dependency labels
* How already-applied upstream stacks are skipped during `ubx apply`
* The `--no-deps` flag to opt out of automatic resolution
* The `--all` flag to plan or apply every stack in the workspace

***

## Why this matters

<Info>
  `@name.output` resolves to a Pulumi `StackReference.getOutput()` call in generated code. ubx reads the reference at compile time to build the dependency graph — no separate config file, no `depends_on` in a YAML manifest.
</Info>

***

## The source code

<CodeGroup>
  ```hcl networking/stack.iac theme={"theme":"css-variables"}
  unit "aws_vpc" "main" {
    cidr_block           = "10.0.0.0/16"
    enable_dns_hostnames = true
  }

  unit "aws_subnet" "private" {
    vpc_id     = unit.aws_vpc.main.id
    cidr_block = "10.0.1.0/24"
  }

  output "vpc_id" {
    value       = unit.aws_vpc.main.id
    description = "VPC for downstream stacks"
  }

  output "private_subnet_ids" {
    value = unit.aws_subnet.private.ids
  }
  ```

  ```hcl cluster/stack.iac theme={"theme":"css-variables"}
  # @networking.vpc_id is a same-repo cross-stack reference.
  # ubx resolves the networking stack automatically — no remote block needed.
  unit "aws_eks_cluster" "main" {
    name       = "my-cluster-${input.env}"
    vpc_config = {
      vpc_id     = @networking.vpc_id
      subnet_ids = @networking.private_subnet_ids
    }
  }

  output "cluster_endpoint" {
    value = unit.aws_eks_cluster.main.endpoint
  }
  ```

  ```hcl cluster/inputs.iac theme={"theme":"css-variables"}
  input "env" {
    type    = "string"
    default = "dev"
  }
  ```

  ```hcl ubx.iac theme={"theme":"css-variables"}
  ubx {
    name    = "my-platform"
    runtime = "typescript"
    stacks  = ["dev", "staging", "prod"]
  }

  backend "s3" {
    bucket = "my-state-bucket"
    region = "eu-west-1"
    key    = "${stack}/my-platform.tfstate"
  }
  ```
</CodeGroup>

***

## How it works

<Steps>
  <Step title="@name.output is the only config you need">
    Writing `@networking.vpc_id` in the cluster stack is the complete declaration of the dependency. ubx parses this at compile time, identifies `networking` as the name of a sibling stack directory, and adds it to the dependency graph. There is no separate orchestration file to maintain.
  </Step>

  <Step title="ubx resolves the graph before planning or applying">
    When you run `ubx plan --env dev` in the cluster directory, ubx first prints the resolved graph, then plans each stack in order — networking first as a dependency, cluster second as the target. Each stack gets its own `━━━`-bounded section in the output with a `(N/M — role)` label.
  </Step>

  <Step title="Already-applied stacks are skipped">
    During `ubx apply`, ubx checks whether each upstream stack has already been applied and has no pending changes. If networking is up-to-date, its section shows `● networking/  up-to-date` and ubx moves straight to applying the cluster stack. Re-applying a dependency stack unnecessarily is never the default.
  </Step>
</Steps>

***

## Plan output

Running `ubx plan --env dev` from the cluster directory:

```
● Resolving  dependency graph
             networking → cluster

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
● networking/    (1/2 — dependency)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

● Compile   networking/ → .ubx/index.ts
● Stack     dev · eu-west-1
● Packages  ready

────────────────────────────────────────────────────

+ aws_vpc.main          will be created
+ aws_subnet.private    will be created

────────────────────────────────────────────────────
  +2 to create    ~0 to change    -0 to destroy
────────────────────────────────────────────────────

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
● cluster/       (2/2 — target)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

● Compile   cluster/ → .ubx/index.ts
● Stack     dev · eu-west-1
● Packages  ready

────────────────────────────────────────────────────

+ aws_eks_cluster.main  will be created

────────────────────────────────────────────────────
  +1 to create    ~0 to change    -0 to destroy
────────────────────────────────────────────────────

  ubx apply --env dev
```

***

## Apply output

Running `ubx apply --all --env dev` from the repository root:

```
● Resolving  dependency graph
             networking → cluster

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
● networking/    (1/2 — dependency)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

+ aws_vpc.main          done   3s
+ aws_subnet.private    done   1s

✓  networking/  done in 5s · 2 created · 0 changed · 0 destroyed

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
● cluster/       (2/2 — target)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

+ aws_eks_cluster.main  done  4m 12s

✓  cluster/  done in 4m 13s · 1 created · 0 changed · 0 destroyed
```

***

## What ubx generates

<CodeGroup>
  ```typescript cluster/.ubx/index.ts theme={"theme":"css-variables"}
  // Generated by ubx — do not edit
  import * as pulumi from "@pulumi/pulumi";
  import * as aws from "@pulumi/aws";

  const config = new pulumi.Config();
  const env = config.get("env") || "dev";

  // @networking — same-repo StackReference (resolved automatically)
  const networkingStack = new pulumi.StackReference(
    `myorg/networking/${env}`
  );

  // @networking.vpc_id → networkingStack.getOutput("vpc_id")
  // @networking.private_subnet_ids → networkingStack.getOutput("private_subnet_ids")
  const main = new aws.eks.Cluster("main", {
      name: pulumi.interpolate`my-cluster-${env}`,
      vpcConfig: {
          vpcId: networkingStack.getOutput("vpc_id") as pulumi.Output<string>,
          subnetIds: networkingStack.getOutput("private_subnet_ids") as pulumi.Output<string[]>,
      },
  });

  export const clusterEndpoint = main.endpoint;
  ```
</CodeGroup>

***

## Common mistakes

<Warning>
  `@name.output` resolves sibling stacks by directory name, not by the `name` attribute in `ubx.iac`. If your networking stack directory is named `net/` but you write `@networking.vpc_id`, ubx will not find it. Use the actual directory name: `@net.vpc_id`.
</Warning>

<Tip>
  Use `--no-deps` to plan or apply only the target stack without resolving dependencies. This is useful in CI pipelines where each stack has its own job and upstream stacks are guaranteed to have been applied already: `ubx apply --no-deps --env prod`.
</Tip>

***

## Run it

```bash theme={"theme":"css-variables"}
# From the cluster/ directory — resolves and plans networking first
ubx plan --env dev

# Apply all stacks in dependency order from the repo root
ubx apply --all --env dev --yes

# Apply only the cluster stack, skip dependency resolution
ubx apply --no-deps --env dev --yes

# Plan only, skip dependency resolution
ubx plan --no-deps --env dev
```

***

## What you learned

<Check>`@name.output` cross-stack references are the complete declaration of a stack dependency — no orchestration file needed</Check>
<Check>ubx resolves the dependency graph at compile time and plans/applies stacks in topological order automatically</Check>
<Check>Already-applied upstream stacks with no changes are detected and skipped during `ubx apply`</Check>
<Check>`--no-deps` opts out of automatic resolution for CI pipelines with explicit per-stack job ordering</Check>

***

## Next steps

<CardGroup cols={2}>
  <Card title="remote — cross-repo references" href="/v1/tutorials/34-remote-stack-reference">
    Reference outputs from stacks in other repositories
  </Card>

  <Card title="interface contract validation" href="/v1/tutorials/35-interface-contract-validation">
    Enforce typed output contracts on stacks
  </Card>
</CardGroup>

***

<Info>
  Full runnable example: [github.com/ubiquex/ubx-examples/59-multi-stack-dependency-resolution](https://github.com/ubiquex/ubx-examples/tree/main/59-multi-stack-dependency-resolution)
</Info>
