A single flat template does not scale past a certain size or a certain number of teams touching it. CloudFormation gives you two ways to split a large deployment into smaller pieces: nested stacks, where one stack embeds others as resources, and cross-stack references, where independent stacks share values through exported outputs.
They solve similar-sounding problems but compose differently, and knowing which one fits is the point of this page.
A nested stack is declared like any other resource, using the type AWS::CloudFormation::Stack, with TemplateURL pointing at the child template's location in S3.
Note the child template must already exist at that TemplateURL in S3 before the parent stack is created - the parent references it by location, it does not embed the file inline.
Cross-stack references work differently: two stacks that are not parent and child at all, connected only through an Outputs{Export} in one and Fn::ImportValue in the other.
The exporting stack must exist and finish creating before the importing stack is deployed, since Fn::ImportValue resolves at deploy time and fails if the export does not exist yet.
Even though a nested stack is declared as a resource inside its parent, it is internally a real, independent stack with its own StackId, its own StackStatus, and its own entry in DescribeStacks. Drift detection, described earlier in this section, runs per-stack, so a nested stack can drift independently of its parent, and you may need to call DetectStackDrift on it directly (or use DetectStackDrift's nested-stack support, which CloudFormation extends to child stacks automatically for direct parents).
Deleting or updating the parent cascades to its nested stacks - deleting the parent deletes the children, and updating the parent's template can update, replace, or remove children in the same operation.
An export cannot be removed or changed while another stack still imports it. If you try to update or delete the exporting stack in a way that would break the export, UpdateStack or DeleteStack fails outright, listing the importing stacks as the reason.
# --- Python (boto3) ---import boto3cfn = boto3.client("cloudformation", region_name="us-east-1")resp = cfn.list_imports(ExportName="shared-vpc-id")print(resp["Imports"]) # stack names currently importing this value
// --- TypeScript (AWS SDK v3) ---import { CloudFormationClient, ListImportsCommand } from "@aws-sdk/client-cloudformation";const cfn = new CloudFormationClient({ region: "us-east-1" });const resp = await cfn.send(new ListImportsCommand({ ExportName: "shared-vpc-id" }));console.log(resp.Imports); // stack names currently importing this value
ListImports is the way to check this before attempting a risky change - it tells you exactly which stacks depend on an export, so you can update or migrate them off it first.
Nested stacks fit reusable sub-templates that only make sense within one larger deployment - a standard VPC layout embedded in several application stacks, for instance - where the parent owns the child's full lifecycle. Cross-stack references fit genuine boundaries between independently owned stacks, such as a platform team's networking stack exporting a VPC ID that several separate application teams' stacks import.
Forgetting the child template must already be in S3. A nested stack's TemplateURL is resolved at parent creation time - upload the child template before creating the parent.
Deleting an exporting stack with active imports.DeleteStack (and any update that removes the export) fails until every importing stack stops relying on it - check with ListImports first.
Treating nested stacks as free composition. Deleting or replacing a parent cascades to its children; a nested stack is not an isolated unit you can delete independently while the parent still declares it.
Circular cross-stack imports. Two stacks that try to import each other's exports cannot both deploy first - restructure so dependencies flow one direction.
Assuming drift detection covers nested stacks for free. It generally does for direct nested stacks, but always confirm coverage per resource type rather than assuming full recursive coverage on a deeply nested tree.
A single flat template for small, cohesive stacks where the composition overhead of nesting or exports outweighs the benefit.
CDK constructs with cross-stack references (exportValue / automatic parameter passing) when the same composition is being authored in code rather than raw templates - the CDK generates the export/import wiring for you at synthesis time.
StackSets when the same template needs to be deployed as many independent stacks across accounts or regions, rather than composed within one account.
SSM Parameter Store as a looser alternative to Fn::ImportValue for sharing values across stacks that should not be hard-linked by CloudFormation's export-lock behavior.
Reach for nested stacks and cross-stack exports when composition genuinely reduces duplication; a flat template is simpler and often good enough below a certain size.
A stack declared as a resource (AWS::CloudFormation::Stack) inside a parent stack's template, with its own template referenced by TemplateURL. It is internally a separate stack, but its lifecycle is tied to the parent.
How is a cross-stack reference different from a nested stack?
Cross-stack references connect two independent stacks via Outputs{Export} and Fn::ImportValue - neither owns the other's lifecycle. Nested stacks are owned and cascaded by their parent.
Why did my stack deletion fail with an export error?
Another stack is still importing that export via Fn::ImportValue. Use ListImports to find which stacks, then update them to stop importing it before deleting or changing the export.
Does deleting a parent stack delete its nested stacks?
Yes. Nested stacks are cascaded with the parent - deleting or updating the parent can delete, replace, or update its nested stacks in the same operation.
Can I detect drift on a nested stack?
Yes, CloudFormation generally extends drift detection to direct nested stacks along with the parent, but confirm per resource type rather than assuming full recursive coverage on deeply nested trees.
When should I use nested stacks instead of exports?
When the child template is a reusable piece that only makes sense within one larger deployment and should share the parent's lifecycle - not for sharing values across independently owned stacks.
Can two stacks import each other's exports?
No, not simultaneously - that is a circular dependency and neither stack can deploy first. Restructure so the dependency flows one direction.
Is there a looser alternative to Fn::ImportValue?
SSM Parameter Store, read at deploy time, avoids CloudFormation's export-lock behavior at the cost of losing the safety check that prevents deleting a value still in use.