skip to content

In the AWS CDK, when is a `NestedStack` the right choice instead of another top-level `Stack`, and what do you give up by nesting?

level: seniorimportance: nice to knowfreq 30%

answer

  1. one resource in the parent template
  2. parameters and outputs, not exports
  3. the parent is the deployment unit
  4. failure rolls back the parent
  5. cadence decides, not tidiness

basics

~20 s

A NestedStack becomes a single AWS::CloudFormation::Stack resource inside its parent, which buys room under the per-stack resource limit and parameter-based wiring instead of exports. You give up independent deployment: every change goes through a parent update.

solid answer

~50 s

A `NestedStack` is not a peer of the stack it lives in — it synthesizes into a single `AWS::CloudFormation::Stack` resource in the parent template, with its own template published as an asset. Two things make that attractive: the whole child counts as **one** resource against the parent's CloudFormation resource limit, and values crossing the boundary become stack **parameters and outputs** rather than account-wide exports, so you never hit the export-in-use deadlock when refactoring. What you give up is independence. The deployment target is always the parent: you cannot deploy, roll back or destroy the child on its own, a failure inside it fails the parent's update, and its resources are less visible in diffs and in the console. So nesting suits a large reusable pattern that must always ship atomically with its parent; anything with its own release cadence should be a top-level stack.

code

typescript · 19 lines
typescript
import * as cdk from 'aws-cdk-lib';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { Construct } from 'constructs';

class QueueTier extends cdk.NestedStack {
  public readonly queue: sqs.Queue;
  constructor(scope: Construct, id: string, props?: cdk.NestedStackProps) {
    super(scope, id, props);
    this.queue = new sqs.Queue(this, 'Work');
  }
}

class ParentStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: cdk.StackProps) {
    super(scope, id, props);
    const tier = new QueueTier(this, 'Tier'); // one AWS::CloudFormation::Stack resource
    new cdk.CfnOutput(this, 'QueueUrl', { value: tier.queue.queueUrl });
  }
}

go deeper

for a junior

Know that a nested stack appears as a single AWS::CloudFormation::Stack resource inside its parent's template and is created as part of deploying the parent.

for a middle

Explain the two concrete benefits — one resource against the parent's limit, and parameter/output wiring instead of exports — and that the parent is always the deployment target.

for a senior

Argue the choice from operations: independent deploy and rollback versus atomicity and blast radius, and use release cadence rather than tidiness to decide where the boundary goes.

for a principal

Own the estate convention: which units are separately deployable, how much cross-stack wiring the organisation is willing to operate, and why constructs, not nested stacks, are the answer to code structure.

## What a NestedStack synthesizes to A top-level `Stack` becomes a template that the CLI deploys directly. A `NestedStack` becomes something different: in the **parent's** template it appears as a single resource of type `AWS::CloudFormation::Stack`, whose `TemplateURL` points at the child's template, published as an asset alongside the app's other assets. CloudFormation then creates the child stack as part of the parent's create or update operation. So the relationship is containment, not siblinghood, and every operational property follows from that. ## What nesting buys **Resource-count headroom.** CloudFormation caps the number of resources in one stack. A nested stack collapses into one resource in the parent, so a pattern that expands into fifty resources costs the parent one slot. This is the original reason nested stacks exist, and it is why some higher-level patterns use them internally. **Wiring without exports.** Values passed from parent to child become CloudFormation **Parameters** on the child; values passed back become **Outputs** the parent reads. The CDK generates both. Crucially, none of this uses `Export`/`Fn::ImportValue`, so the account-wide export namespace is untouched and you never face the situation where an export cannot be removed because another stack still imports it. Refactoring a nested boundary is far easier than refactoring a cross-stack one. **Atomicity.** Parent and child update as one operation. If the child fails, the parent's update rolls back, and you are never left with half of a logical unit deployed. ## What nesting costs **No independent deployment.** The CLI's deployment unit is the top-level stack. You cannot deploy just the child, and you cannot roll it back or destroy it on its own — the child's lifecycle is entirely the parent's. Every change to a resource inside it puts the parent stack into `UPDATE_IN_PROGRESS`. **Bigger blast radius on failure.** The flip side of atomicity: a failure deep inside a nested stack fails the parent update and rolls back the parent's changes too, including unrelated changes made in the same deploy. **Less visibility.** In the console a nested stack is a separate stack marked as nested, and in `cdk diff` you get less immediate detail than for a top-level stack, so reviewing a change is slightly harder. Operators who go looking for a resource have one more hop to make. **Structural limits.** CloudFormation limits how deeply stacks may nest, so deep hierarchies are not an option, and the child's template must be uploadable as an asset — which means the environment has to be bootstrapped, exactly as for other assets. ## When to choose which Ask one question first: **does this group of resources have its own release cadence?** If yes — a service that redeploys several times a day, a network layer that changes twice a year, anything a different team owns — make it a **top-level stack**. You accept cross-stack references and their refactoring friction in exchange for the ability to deploy and roll back the piece by itself. That independence is usually the more valuable property, which is why most CDK estates are mostly top-level stacks. If no — the group only ever changes together with its parent, is generated by a reusable pattern, or exists mainly to keep the parent under the resource limit — a **nested stack** is a reasonable, low-drama choice. Its parameter/output wiring is genuinely nicer than exports. And if the answer is "this is just code organisation", the right tool is neither: define your own construct class. Grouping resources in a construct costs nothing operationally, because those resources still land in whichever stack the construct is scoped under. Reaching for `NestedStack` when you only wanted tidier code adds a real CloudFormation boundary you will have to operate forever. ```typescript class Ingestion extends cdk.NestedStack { constructor(scope: Construct, id: string, props?: cdk.NestedStackProps) { super(scope, id, props); // dozens of resources; counts as ONE resource in the parent template } } ``` ## The summary an interviewer wants Nesting trades independent deployability for a smaller footprint in the parent and easier wiring. Top-level stacks trade wiring friction for the ability to change one part of the estate without touching the rest. Neither is a default; the release cadence of the resources decides.

  • How do values cross the boundary between a parent stack and its nested stack in the CDK?
    As CloudFormation Parameters going in and Outputs coming back, generated automatically by the CDK. That is why nesting avoids the export-in-use problem entirely: no account-wide `Export` name is created, so removing a reference is an ordinary update of both templates in a single deployment.
  • What happens when a resource inside a nested stack fails to create during an update?
    The nested stack's operation fails, which fails the parent's update, and the parent rolls back — including other, unrelated changes deployed at the same time. You cannot retry only the child, because the CLI has no separate deployment target for it; you fix the cause and redeploy the parent.
  • If you only want tidier code, is a nested stack the right tool?
    No. Define your own construct class instead. A construct groups resources with zero operational cost, since they still belong to whichever stack the construct is scoped under. A nested stack creates a real CloudFormation boundary — an extra stack to operate, diff and reason about — which is a poor price for code organisation.
  • Why do some higher-level CDK patterns create nested stacks internally?
    Because a pattern can expand into many resources, and collapsing them into one `AWS::CloudFormation::Stack` resource keeps the consumer's stack well under the CloudFormation per-stack resource limit. It also keeps the pattern's internals wired by parameters and outputs rather than adding exports to the consumer's account.

saying these in an interview costs you the question

  • Says a nested stack can be deployed on its own with cdk deploy
  • Thinks nesting is just a code-organisation feature
  • Believes nested stacks use exports and Fn::ImportValue
  • Claims a failure in the child leaves the parent untouched
  • Reaches for nesting instead of writing a construct class

context