Skip to main content

Layered Stack Configuration

Atmos Design Pattern

The Layered Stack Configuration pattern organizes components into infrastructure layers based on their function—similar to how network architectures have distinct layers (network, transport, application). Each layer groups related components and can be managed independently.

Use-cases

Use the Layered Stack Configuration pattern when:

  • You have many components that naturally group by function (networking, data, compute, etc.)

  • Different teams own different layers of infrastructure

  • You want to import only the layers needed for a specific environment

Benefits

The Layered Stack Configuration pattern provides the following benefits:

  • Components are organized by their infrastructure function, making configs intuitive to navigate

  • Teams can own specific layers using tools like GitHub's CODEOWNERS

  • Stacks can import only the layers they need (e.g., a dev environment might skip the monitoring layer)

  • Changes to one layer don't affect other layers

Example

The following example organizes infrastructure into three layers: network, data, and compute.

Directory Structure

   ├── stacks
│ ├── catalog # component defaults
│ │ ├── vpc
│ │ │ └── defaults.yaml
│ │ ├── rds
│ │ │ └── defaults.yaml
│ │ └── eks
│ │ └── defaults.yaml
│ ├── layers # infrastructure layers
│ │ ├── network.yaml
│ │ ├── data.yaml
│ │ └── compute.yaml
│ └── deploy
│ ├── dev.yaml
│ └── prod.yaml

└── components
└── terraform
├── vpc
├── rds
└── eks

Configure atmos.yaml

atmos.yaml
components:
terraform:
base_path: "components/terraform"

stacks:
base_path: "stacks"
included_paths:
- "deploy/**/*"
name_template: "{{.vars.stage}}"

Define the Layers

Each layer imports the component defaults for that infrastructure function:

stacks/layers/network.yaml
import:
- catalog/vpc/defaults

# Network layer: VPC, subnets, security groups, etc.
stacks/layers/data.yaml
import:
- catalog/rds/defaults

# Data layer: databases, caches, message queues, etc.
stacks/layers/compute.yaml
import:
- catalog/eks/defaults

# Compute layer: EKS, ECS, Lambda, etc.

Import Layers into Stacks

Stacks import the layers they need:

stacks/deploy/dev.yaml
import:
- layers/network
- layers/data
- layers/compute

vars:
stage: dev

# Dev-specific overrides
components:
terraform:
rds:
vars:
instance_class: db.t3.small
stacks/deploy/prod.yaml
import:
- layers/network
- layers/data
- layers/compute

vars:
stage: prod

# Prod-specific overrides
components:
terraform:
rds:
vars:
instance_class: db.r6g.xlarge
multi_az: true

Common Layer Examples

Here are typical infrastructure layers you might define:

LayerComponentsPurpose
networkVPC, subnets, NAT, VPNFoundation networking
securityWAF, security groups, KMSSecurity controls
dataRDS, ElastiCache, S3Data storage
computeEKS, ECS, EC2Application runtime
observabilityCloudWatch, DatadogMonitoring and logging

Selective Layer Import

Not every environment needs every layer. For example, a minimal dev environment might skip observability:

stacks/deploy/dev.yaml
import:
- layers/network
- layers/compute
# Skip observability in dev

vars:
stage: dev

While production includes everything:

stacks/deploy/prod.yaml
import:
- layers/network
- layers/security
- layers/data
- layers/compute
- layers/observability

vars:
stage: prod

Layer Dependencies

Layers often have implicit dependencies. The compute layer depends on the network layer being provisioned first. Document or enforce this ordering in your deployment process:

# Deploy in layer order
atmos terraform apply vpc -s prod
atmos terraform apply rds -s prod
atmos terraform apply eks -s prod

References