# YAML Configuration Reference

YAML is the configuration language for Atmos stacks. Understanding YAML's features, gotchas, and best practices is essential for writing maintainable stack configurations at scale.

This guide covers everything you need to know about YAML in Atmos, from anchors and references to avoiding common pitfalls when mixing YAML with Go templates.

## YAML is a Superset of JSON

**Any valid JSON is also valid YAML.** This means you can embed JSON objects anywhere in your YAML configuration:

**File:** `stacks/prod.yaml`

```yaml
vars:
  # YAML-style map
  tags:
    Environment: production
    Team: platform

  # JSON-style map (also valid!)
  metadata: {"created": "2024-01-01", "version": "1.0"}

components:
  terraform:
    vpc:
      # Mixing YAML and JSON
      vars:
        cidr_block: "10.0.0.0/16"
        tags: {"Name": "prod-vpc", "ManagedBy": "atmos"}
```

**When to use JSON in YAML:**

- ✅ Compact, single-line objects
- ✅ Generated configuration from scripts
- ✅ Copying from JSON sources

**When to use YAML syntax:**

- ✅ Multi-line configuration (more readable)
- ✅ Configuration that needs comments
- ✅ Nested structures (cleaner indentation)

## Dot Notation for Nested Keys

When you only need to set a single value deep in a nested structure, you can use **dot notation** instead of creating the full hierarchy. This makes your configuration more concise and readable.

### Traditional Nesting vs Dot Notation

**Traditional approach (verbose):**

**File:** `stacks/prod.yaml`

```yaml
components:
  terraform:
    vpc:
      metadata:
        component: vpc-base
        inherits:
          - vpc-defaults
      settings:
        spacelift:
          workspace_enabled: true
          autodeploy: false
        validation:
          check_cloudformation: true
```

**Dot notation (concise):**

**File:** `stacks/prod.yaml`

```yaml
components:
  terraform:
    vpc:
      metadata.component: vpc-base
      metadata.inherits:
        - vpc-defaults
      settings.spacelift.workspace_enabled: true
      settings.spacelift.autodeploy: false
      settings.validation.check_cloudformation: true
```

Both configurations produce the exact same result. Dot notation is purely a syntax convenience for writing cleaner YAML.

### Mixing Dot Notation and Traditional Nesting

You can mix both approaches in the same configuration. Use dot notation for single values, traditional nesting for groups:

**File:** `stacks/prod.yaml`

```yaml
components:
  terraform:
    vpc:
      # Dot notation for single deep values
      metadata.component: vpc-base
      settings.spacelift.workspace_enabled: true

      # Traditional nesting for related groups
      vars:
        cidr_block: "10.0.0.0/16"
        enable_dns_hostnames: true
        enable_dns_support: true
        availability_zones:
          - us-east-1a
          - us-east-1b
          - us-east-1c

      # Dot notation again for another single value
      settings.validation.check_cloudformation: true
```

### When to Use Dot Notation

**✅ Use dot notation when:**

- Setting a single value deep in the tree
- You want more concise, readable configuration
- The nested path is clear and self-documenting

**❌ Use traditional nesting when:**

- Setting multiple related values in the same section
- You want to emphasize the structure
- You're defining complex nested objects with many fields

### Dot Notation Works Everywhere

Dot notation isn't limited to component configuration—it works in any YAML section:

**File:** `stacks/globals.yaml`

```yaml
# Top-level vars with dot notation
vars.tags.ManagedBy: Atmos
vars.tags.Environment: production

# Settings with dot notation
settings.spacelift.workspace_enabled: true
settings.github.default_branch: main

# Environment variables
env.TF_LOG: DEBUG
env.AWS_DEFAULT_REGION: us-east-1
```

This is equivalent to:

**File:** `stacks/globals.yaml`

```yaml
vars:
  tags:
    ManagedBy: Atmos
    Environment: production

settings:
  spacelift:
    workspace_enabled: true
  github:
    default_branch: main

env:
  TF_LOG: DEBUG
  AWS_DEFAULT_REGION: us-east-1
```

:::tip Best Practice
Use dot notation when it improves readability. If you have 2+ values in the same section, traditional nesting is often clearer.
:::

## Anchors and References (Aliases)

YAML anchors (`&`) and references (`*`) let you reuse configuration within a single file. This is YAML's built-in DRY mechanism.

### Basic Anchor and Reference

**File:** `stacks/prod.yaml`

```yaml
# Define an anchor with &
vars:
  common_tags: &common_tags
    ManagedBy: Atmos
    Environment: production
    Team: Platform

components:
  terraform:
    vpc:
      vars:
        # Reference the anchor with *
        tags: *common_tags

    eks:
      vars:
        # Same tags reused
        tags: *common_tags
```

**Result after YAML processing:**

```yaml
components:
  terraform:
    vpc:
      vars:
        tags:
          ManagedBy: Atmos
          Environment: production
          Team: Platform
    eks:
      vars:
        tags:
          ManagedBy: Atmos
          Environment: production
          Team: Platform
```

### Merging with `<<` (Merge Key)

The merge key `<<` combines anchors with additional keys:

**File:** `stacks/prod.yaml`

```yaml
vars:
  base_tags: &base_tags
    ManagedBy: Atmos
    Environment: production

components:
  terraform:
    vpc:
      vars:
        tags:
          <<: *base_tags  # Merge base_tags
          Name: prod-vpc  # Add additional keys
          Type: network

    rds:
      vars:
        tags:
          <<: *base_tags
          Name: prod-database
          Type: database
```

**Result:**

```yaml
components:
  terraform:
    vpc:
      vars:
        tags:
          ManagedBy: Atmos
          Environment: production
          Name: prod-vpc
          Type: network
```

### Real-World Example: Component Defaults

**File:** `stacks/prod-us-east-1.yaml`

```yaml
# Define VPC defaults as an anchor
vars: &vpc_defaults
  enable_dns_hostnames: true
  enable_dns_support: true
  enable_nat_gateway: true
  single_nat_gateway: false
  create_igw: true
  map_public_ip_on_launch: false

components:
  terraform:
    vpc-prod:
      metadata:
        component: vpc
      vars:
        <<: *vpc_defaults  # Reuse VPC defaults
        cidr_block: "10.0.0.0/16"
        availability_zones: ["us-east-1a", "us-east-1b", "us-east-1c"]
        single_nat_gateway: true  # Override for prod
```

:::tip
For cross-file reuse, use [Atmos imports and inheritance](/howto/inheritance) instead of YAML anchors.
:::

### Multiple Anchors with Merge

You can merge multiple anchors:

**File:** `stacks/prod.yaml`

```yaml
vars:
  security_tags: &security_tags
    Compliance: SOC2
    DataClassification: sensitive

  cost_tags: &cost_tags
    CostCenter: engineering
    Project: infrastructure

components:
  terraform:
    rds:
      vars:
        tags:
          <<: [*security_tags, *cost_tags]  # Merge both
          Name: prod-database
```

**Result:**

```yaml
tags:
  Compliance: SOC2
  DataClassification: sensitive
  CostCenter: engineering
  Project: infrastructure
  Name: prod-database
```

:::warning Anchor Scope
Anchors are **file-scoped only**. They don't work across imports. Use Atmos imports and inheritance for cross-file reuse.
:::

## Type Coercion Gotchas

YAML automatically converts values based on their appearance. This causes unexpected behavior if you're not careful.

### The `NO` Problem (Country Codes)

**File:** `stacks/countries.yaml (WRONG)`

```
vars:
  allowed_countries:
    - US
    - GB
    - NO  # ❌ Treated as boolean false!
    - SE
    - DK
```

**What YAML sees:**

```yaml
vars:
  allowed_countries:
    - US
    - GB
    - false  # NO became false!
    - SE
    - DK
```

**Fix: Quote it**

**File:** `stacks/countries.yaml (CORRECT)`

```
vars:
  allowed_countries:
    - US
    - GB
    - "NO"  # ✅ String
    - SE
    - DK
```

### Boolean Conversion Table

YAML interprets these as booleans:

| String | Becomes |
|--------|---------|
| `yes`, `Yes`, `YES` | `true` |
| `no`, `No`, `NO` | `false` |
| `true`, `True`, `TRUE` | `true` |
| `false`, `False`, `FALSE` | `false` |
| `on`, `On`, `ON` | `true` |
| `off`, `Off`, `OFF` | `false` |

**Always quote these if you need them as strings:**

**File:** `stacks/config.yaml`

```yaml
vars:
  # WRONG - Becomes boolean
  confirmation: yes  # → true

  # CORRECT - Stays string
  confirmation: "yes"  # → "yes"

  # Country codes that need quoting
  countries: ["NO", "ON", "OFF", "YES"]
```

### Numeric Type Coercion

**File:** `stacks/config.yaml`

```yaml
vars:
  # These become numbers, not strings
  version: 1.20       # → 1.2 (float, trailing zero lost!)
  zip_code: 07094     # → 7094 (integer, leading zero lost!)
  port: 8080          # → 8080 (integer)

  # Quote to preserve as strings
  version: "1.20"     # → "1.20"
  zip_code: "07094"   # → "07094"
  port: "8080"        # → "8080"
```

### Octal Number Trap

**File:** `stacks/config.yaml`

```yaml
vars:
  # Leading zero = octal in YAML 1.1!
  file_mode: 0644     # → 420 in decimal (octal conversion!)

  # Quote to preserve
  file_mode: "0644"   # → "0644"
```

### Explicit Typing

When quoting isn't desired, you can use YAML's explicit type tags to force a specific type. This is an alternative to quoting:

**File:** `stacks/config.yaml`

```yaml
vars:
  # Force string type (alternative to quoting)
  country: !!str NO           # String "NO" (not boolean)
  version: !!str 1.20         # String "1.20" (not float)
  zip_code: !!str 07094       # String "07094" (leading zero preserved)

  # Force numeric types
  port: !!int "8080"          # Integer 8080 (from quoted string)
  ratio: !!float "1.5"        # Float 1.5

  # Explicit boolean
  enabled: !!bool "true"      # Boolean true
```

**When to use explicit typing vs quoting:**
✅ Use quotes (`"value"`) - Simpler, more common, widely understood
✅ Use type tags (`!!str`) - When you need to be explicit about intent, or in generated YAML

:::tip
Most Atmos users prefer quoting over type tags. Type tags are useful when generating YAML programmatically or when you want to be very explicit about types.
:::

## Quoting Best Practices

### When You MUST Quote

**1. Strings that look like other types:**

```yaml
vars:
  country: "NO"           # Looks like boolean
  version: "1.20"         # Looks like number
  value: "true"           # Looks like boolean
  code: "07094"           # Leading zero
```

**2. Strings with special characters:**

```yaml
vars:
  url: "https://example.com"     # Contains :
  path: "/var/log/app.log"       # Fine, but quote for safety
  regex: "^[a-z]+$"               # Contains special chars
  template: "{{ .vars.name }}"    # Contains braces
```

**3. Multi-line strings (see next section)**

### Consistent Quoting Strategy

**Recommended: Quote all string values for consistency**

**File:** `stacks/prod.yaml (Consistent Style)`

```
vars:
  environment: "production"       # Quoted
  region: "us-east-1"             # Quoted
  namespace: "myapp"              # Quoted
  cidr_block: "10.0.0.0/16"       # Quoted
  enable_vpc: true                # Boolean (no quotes)
  instance_count: 3               # Number (no quotes)
```

**Benefits:**

- ✅ No mental overhead deciding when to quote
- ✅ Prevents type coercion surprises
- ✅ Easier to read and maintain
- ✅ Tools like prettier/yamllint can enforce it

## Multi-Line Strings

YAML has multiple ways to handle multi-line strings. Choose the right one for your use case.

### Literal Block Scalar (`|`)

Preserves line breaks and trailing newline:

**File:** `stacks/config.yaml`

```yaml
vars:
  script: |
    #!/bin/bash
    set -e
    echo "Starting deployment"
    terraform apply -auto-approve
```

**Result:**

```
#!/bin/bash\nset -e\necho "Starting deployment"\nterraform apply -auto-approve\n
```

### Folded Block Scalar (`>`)

Folds lines into a single line, preserves final newline:

**File:** `stacks/config.yaml`

```yaml
vars:
  description: >
    This is a very long description that spans
    multiple lines but will be folded into a
    single line when processed.
```

**Result:**

```
This is a very long description that spans multiple lines but will be folded into a single line when processed.\n
```

### Block Chomping (Control Trailing Newlines)

**`|` or `>` alone:** Keep final newline

**`|-` or `>-`:** Strip final newline (clip)

**`|+` or `>+`:** Keep all trailing newlines (keep)

**File:** `stacks/config.yaml`

```yaml
vars:
  # Default: Keep final newline
  script_keep: |
    echo "hello"
    echo "world"

  # Strip trailing newline
  script_strip: |-
    echo "hello"
    echo "world"

  # Keep all trailing newlines
  script_keep_all: |+
    echo "hello"
    echo "world"
```

### Real-World Example: User Data Script

**File:** `stacks/prod.yaml`

```yaml
components:
  terraform:
    ec2:
      vars:
        user_data: |
          #!/bin/bash
          set -euo pipefail

          # Install dependencies
          apt-get update
          apt-get install -y docker.io

          # Start application
          docker run -d -p 80:80 myapp:latest
```

## Go Templates in YAML (Gotchas!)

Atmos uses Go templates for dynamic configuration. **Go template braces conflict with YAML flow-style maps.**

### The Brace Problem

**File:** `stacks/config.yaml (WRONG)`

```
vars:
  # ❌ YAML sees this as incomplete flow-style map!
  bucket_name: {{ .vars.namespace }}-{{ .vars.environment }}-bucket
```

**Error:**

```
Error parsing YAML: mapping values are not allowed in this context
```

### Solution: Quote Template Strings

**File:** `stacks/config.yaml (CORRECT)`

```
vars:
  # ✅ Quote any string containing Go templates
  bucket_name: "{{ .vars.namespace }}-{{ .vars.environment }}-bucket"

  # ✅ Also works for complex expressions
  cluster_name: '{{ printf "%s-%s-eks" .vars.namespace .vars.region }}'

  # ✅ Multi-line templates need literal block with quotes
  complex_value: |
    {{ range .vars.availability_zones }}
    - {{ . }}
    {{ end }}
```

### Template Delimiters and YAML Conflicts

**Problem: YAML flow-style syntax looks like templates:**

**File:** `stacks/config.yaml`

```yaml
vars:
  # This is a YAML flow-style map (not a template!)
  inline_map: {key: value, another: thing}

  # This is a Go template (quote it!)
  templated: "{{ .vars.value }}"
```

**Best practice: Avoid flow-style maps when using templates**

**File:** `stacks/config.yaml (Better)`

```
vars:
  # Use block-style maps for clarity
  inline_map:
    key: value
    another: thing

  # Templates are clearly quoted
  templated: "{{ .vars.value }}"
```

### Nested Templates in Lists

**File:** `stacks/prod.yaml`

```yaml
components:
  terraform:
    vpc:
      vars:
        # ✅ Each template quoted
        availability_zones:
          - "{{ .vars.region }}a"
          - "{{ .vars.region }}b"
          - "{{ .vars.region }}c"

        # ✅ Complex template in list
        subnet_cidrs:
          - '{{ printf "10.%d.1.0/24" .vars.vpc_index }}'
          - '{{ printf "10.%d.2.0/24" .vars.vpc_index }}'
```

## Common YAML Gotchas

### 1. Indentation Matters (2 Spaces Standard)

**File:** `WRONG: Mixed indentation`

```
components:
  terraform:
	vpc:  # ❌ Tab here
      vars:  # ❌ Spaces here (inconsistent)
        cidr: "10.0.0.0/16"
```

**File:** `CORRECT: Consistent 2-space indentation`

```
components:
  terraform:
    vpc:
      vars:
        cidr: "10.0.0.0/16"
```

**Best practice:**

- ✅ Use 2 spaces (Atmos convention)
- ✅ Configure editor to insert spaces for Tab
- ✅ Never mix tabs and spaces
- ✅ Use `.editorconfig` to enforce (see [EditorConfig Validation](/validation/editorconfig-validation))

### 2. Colons in Unquoted Strings

**File:** `WRONG`

```
vars:
  url: https://example.com  # ❌ Colon confuses parser
  time: 12:30:00            # ❌ Also problematic
```

**File:** `CORRECT`

```
vars:
  url: "https://example.com"  # ✅ Quoted
  time: "12:30:00"            # ✅ Quoted
```

### 3. Lists vs Maps

**File:** `stacks/config.yaml`

```yaml
vars:
  # This is a LIST of maps
  servers:
    - name: web1
      ip: 10.0.1.10
    - name: web2
      ip: 10.0.1.11

  # This is a MAP with nested maps
  server_config:
    web1:
      ip: 10.0.1.10
    web2:
      ip: 10.0.1.11
```

**In Atmos, this matters for deep merge:**

- Maps merge recursively
- Lists replace entirely

### 4. Empty Values, Null Values, and the Tilde (`~`)

In YAML, `~` is a shorthand for `null`. This is part of the YAML 1.1 specification and is commonly used for concise null assignments.

**File:** `stacks/config.yaml`

```yaml
vars:
  # These are ALL equivalent ways to express null:
  null_value: null           # Explicit null
  tilde_value: ~             # Shorthand for null
  no_value:                  # Key with no value = null

  # Example: Setting a value to null (unset it)
  overseer: ~                # Same as overseer: null

  # This is DIFFERENT - it's an empty string, not null:
  empty_string: ""           # Empty string (not null!)
```

:::warning Null vs Empty String
`null` and `""` are **not the same**:

- `null` (or `~`) means "no value" - the key exists but has no value
- `""` means "empty string" - the key has a string value that happens to be empty

This distinction matters in Terraform where `null` triggers default behavior while `""` is an explicit empty value.
:::

**When to use `~` vs `null` vs `""`:**
✅ Use `~` for concise configuration when unsetting values
✅ Use `null` when you want to be explicit about the intent
✅ Use `""` when you need an actual empty string (not null)

**Common use case - Overriding inherited values to null:**

**File:** `stacks/prod.yaml`

```yaml
components:
  terraform:
    vpc:
      vars:
        # Unset an inherited value by setting it to null
        optional_feature: ~      # Removes this from the final config
        legacy_setting: null     # Same effect, more explicit
```

### 5. Duplicate Keys (Last Wins)

**File:** `stacks/config.yaml`

```yaml
vars:
  environment: dev       # This gets overwritten
  region: us-east-1
  environment: prod      # ✅ This wins (last occurrence)
```

**Result:** `environment: prod`

**Best practice:** Use linters to detect duplicates (see [YAML Schema Validation](/validation/json-schema))

## Best Practices for Maintainable YAML

### 1. Consistent Quoting

**File:** `stacks/prod.yaml`

```yaml
# ✅ GOOD: All strings quoted
vars:
  namespace: "myapp"
  environment: "production"
  region: "us-east-1"
  cidr_block: "10.0.0.0/16"
```

### 2. Use Block Style for Nested Data

**File:** `stacks/prod.yaml`

```yaml
# ✅ GOOD: Block style (readable, commentable)
vars:
  tags:
    Environment: production
    Team: platform
    ManagedBy: atmos

# ❌ AVOID: Flow style (compact but hard to read/comment)
vars:
  tags: {Environment: production, Team: platform, ManagedBy: atmos}
```

### 3. Leverage Comments

**File:** `stacks/prod.yaml`

```yaml
components:
  terraform:
    vpc:
      vars:
        # Production VPC requires 3 AZs for HA
        availability_zones:
          - us-east-1a
          - us-east-1b
          - us-east-1c

        # Enable NAT gateways in each AZ (higher cost but redundant)
        single_nat_gateway: false

        # DNS support required for EKS cluster
        enable_dns_hostnames: true
        enable_dns_support: true
```

### 4. Use Anchors for Repeated Config (Within File)

**File:** `stacks/prod.yaml`

```yaml
vars:
  prod_tags: &prod_tags
    Environment: production
    Team: platform
    Compliance: SOC2

components:
  terraform:
    vpc:
      vars:
        tags: *prod_tags

    eks:
      vars:
        tags: *prod_tags
```

### 5. Separate Concerns with Imports (Across Files)

**File:** `stacks/_defaults/tags.yaml`

```yaml
vars:
  common_tags:
    ManagedBy: Atmos
    Team: Platform
```

**File:** `stacks/prod.yaml`

```yaml
import:
  - _defaults/tags

vars:
  environment: production

components:
  terraform:
    vpc:
      vars:
        tags:
          # Inherits common_tags from import
          Environment: production
```

### 6. Use YAML Schema Validation

Configure JSON Schema validation to catch errors:

**File:** `atmos.yaml`

```yaml
schemas:
  atmos:
    manifest: "https://atmos.tools/schemas/atmos/manifest/1.0.json"
  stacks:
    manifest: "https://atmos.tools/schemas/stacks/stack-config/1.0.json"
```

See [JSON Schema Validation](/validation/json-schema) for details.

### 7. Validate YAML with EditorConfig

Use `.editorconfig` to enforce consistent formatting:

**File:** `.editorconfig`

```
[*.{yaml,yml}]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
```

See [EditorConfig Validation](/validation/editorconfig-validation) for details.

## YAML Linting Tools

Use linters to catch issues early:

**yamllint** - General YAML linting

```bash
# Install
pip install yamllint

# Lint stacks
yamllint stacks/
```

**Atmos Validation** - Schema and policy validation

```bash
# Validate stack configurations
atmos validate stacks

# Validate specific component
atmos validate component vpc -s prod
```

See [Validation](/validation/validating) for comprehensive validation strategies.

## Quick Reference: YAML in Atmos

| Feature | Syntax | Use Case |
|---------|--------|----------|
| **Anchor** | `&name` | Define reusable config within file |
| **Reference** | `*name` | Reuse anchored config |
| **Merge** | `<<: *name` | Combine anchor with new keys |
| **Literal Block** | `\|` | Multi-line string, preserve newlines |
| **Folded Block** | `>` | Multi-line string, fold to single line |
| **Null** | `~` or `null` | Explicit null value (unset) |
| **Explicit Typing** | `!!str`, `!!int` | Force specific type (alternative to quoting) |
| **Quote Strings** | `"string"` | Prevent type coercion, escape special chars |
| **Go Template** | `"{{ .vars.x }}"` | Dynamic values (MUST quote) |
| **JSON in YAML** | `{"key": "val"}` | Compact objects (use sparingly) |
| **Comment** | `# comment` | Document configuration |

- **Quote all string values** - Prevents type coercion surprises
- **Quote Go templates** - Avoids YAML parsing conflicts with braces
- **Use 2-space indentation** - Atmos standard, configure your editor
- **Leverage anchors within files** - Use imports across files
- **Prefer block style over flow style** - More readable and commentable
- **Validate early and often** - Use schema validation and linters
- **YAML is JSON-compatible** - But prefer YAML syntax for readability
- **Watch for `NO`, `ON`, `OFF`** - Country codes and words that become booleans

## See Also

- **[YAML in Atmos (Learn)](/learn/yaml)** - Beginner-friendly YAML basics
- **[Stack Configuration](/stacks)** - Advanced stack configuration
- **[Template Functions](/functions/template/)** - Go template functions in Atmos
- **[YAML Functions](/functions/yaml/)** - Native YAML tag functions
- **[JSON Schema Validation](/validation/json-schema)** - Validate YAML structure
- **[EditorConfig Validation](/validation/editorconfig-validation)** - Enforce formatting
