Skip to main content
Tutorial 45 showed the ubx { } block as a standalone config source. But what about projects that already have a ubx.yaml and want to gradually adopt the block syntax, or projects that use YAML for static config and .iac blocks for dynamic config? ubx supports both simultaneously — when it finds both ubx.yaml and a ubx { } block in the same project, it merges them. The .iac block wins on any key that appears in both. Keys present only in ubx.yaml are inherited unchanged. Keys present only in the ubx { } block are applied as additions. The result is a unified config that combines both sources, with a clear info message printed during compilation to confirm both sources are active. This makes dual-format projects transparent rather than silently surprising.

What you’ll learn

  • How ubx.yaml and a ubx { } block merge when both are present
  • The precedence rule: .iac wins on overlap, YAML fills in what .iac doesn’t declare
  • The info message that confirms both sources are active

Why this matters

The merge behaviour enables incremental adoption. A project with an existing ubx.yaml can start adding ubx { } blocks for specific features — AI config, dynamic scan.enabled — without having to migrate the entire config at once. YAML handles the static stuff; the block handles the dynamic stuff.

The source code

# ubx {} overrides runtime (python → typescript) and adds an env tag.
# The team tag from ubx.yaml default_tags is NOT overridden —
# the two tag keys are additive (no overlap here).
ubx {
  runtime = "typescript"   # overrides ubx.yaml runtime: python

  tags {
    env = "${input.env}"   # additive — team tag from ubx.yaml is preserved
  }
}

unit "aws_s3_bucket_v2" "store" {
  bucket = "myapp-${input.env}-store"
}

How it works

1

ubx detects both sources and prints an info message

When both ubx.yaml and a ubx { } block are found, ubx prints: ℹ ubx block in .iac overrides ubx.yaml — both config sources are active. This is always shown — it’s not a warning, just a confirmation that the merge is in effect.
2

.iac wins on overlapping keys

runtime = "typescript" in the ubx { } block overrides runtime: python in ubx.yaml. The final resolved runtime is typescript. ubx.yaml’s runtime: python is silently superseded.
3

Non-overlapping keys are additive

name, description, and default_tags.team from ubx.yaml have no counterpart in the ubx { } block. They’re inherited unchanged. tags.env from the ubx { } block is additive — the final tag set includes both team: "platform" (from YAML) and env: "dev" (from block).

Merge result — what the compiler sees

KeySourceFinal value
nameubx.yaml"ubx-block-vs-ubx-yaml-merge"
runtimeubx { } (wins)"typescript"
descriptionubx.yaml"Shows merge precedence..."
tags.teamubx.yaml"platform"
tags.envubx { }"dev" (or current env)
The compiled stack runs as TypeScript with both team and env tags on every resource.

Common mistakes

The merge is per-key, not per-block. If ubx.yaml has default_tags: { team: platform, env: dev } and the ubx { } block has tags { env = "prod" }, the final env tag is "prod" (block wins) but team is still "platform" (YAML’s value, not overridden). The merge is surgical — only overlapping keys are replaced.
Use ubx validate to confirm the merge result — the info message ℹ ubx block in .iac overrides ubx.yaml — both config sources are active confirms both are present. If you don’t see the message, one of the sources isn’t being detected (check the file is in the project root and named correctly).

Run it

# Both sources active — info message printed
ubx validate
# ℹ  ubx block in .iac overrides ubx.yaml — both config sources are active
# ◆ Compile  stack.iac
# ✓  valid

What you learned

When both ubx.yaml and a ubx { } block are present, ubx merges them — .iac wins on overlap
Non-overlapping keys from each source are preserved additively
The info message always confirms that both sources are active

Next steps

ubx block reference

Full ubx block syntax, all sub-blocks, and the merge specification

Configuration as code

Using ubx block as the sole config source