Live
Black Hat USADark ReadingBlack Hat AsiaAI BusinessAI is making crypto's security problem even worse, Ledger CTO warnsCoinDesk AIIntroducing llmlite: The First Unified LLM Provider Library for the @ZigLang EcosystemDEV CommunityOpenAI Is Bleeding Cash. Its Solution? Military Contracts. - jacobin.comGoogle News: OpenAI‘Cognitive Surrender’ Is a New and Useful Term for How AI Melts BrainsGizmodo‘Cognitive Surrender’ Is a New and Useful Term for How AI Melts Brains - GizmodoGoogle News: AIHow I Built a Production Observability Stack — And Broke It Twice Before It WorkedDEV CommunityMyth of the AI Oracle - LawfareGoogle News: AIVibe Coding Is Dead. Orchestration Is What Comes Next.DEV CommunityUnlocking the Depths of Acting: A Journey Through MethodologiesDEV CommunityMastercard and Google Are Building the Trust Layer for AI That Spends MoneyDEV CommunityHow I Built an Islamic Storytelling App with AI, Audio Narration & 8 LanguagesDEV CommunityAWS CDK Deployment Best PracticesDEV CommunityBlack Hat USADark ReadingBlack Hat AsiaAI BusinessAI is making crypto's security problem even worse, Ledger CTO warnsCoinDesk AIIntroducing llmlite: The First Unified LLM Provider Library for the @ZigLang EcosystemDEV CommunityOpenAI Is Bleeding Cash. Its Solution? Military Contracts. - jacobin.comGoogle News: OpenAI‘Cognitive Surrender’ Is a New and Useful Term for How AI Melts BrainsGizmodo‘Cognitive Surrender’ Is a New and Useful Term for How AI Melts Brains - GizmodoGoogle News: AIHow I Built a Production Observability Stack — And Broke It Twice Before It WorkedDEV CommunityMyth of the AI Oracle - LawfareGoogle News: AIVibe Coding Is Dead. Orchestration Is What Comes Next.DEV CommunityUnlocking the Depths of Acting: A Journey Through MethodologiesDEV CommunityMastercard and Google Are Building the Trust Layer for AI That Spends MoneyDEV CommunityHow I Built an Islamic Storytelling App with AI, Audio Narration & 8 LanguagesDEV CommunityAWS CDK Deployment Best PracticesDEV Community
AI NEWS HUBbyEIGENVECTOREigenvector

AWS CDK Deployment Best Practices

DEV Communityby Kenta GotoApril 5, 202617 min read0 views
Source Quiz

Best Practices for AWS CDK Deployment In AWS CDK, you can define and deploy stacks in various ways. However, with so many approaches available, it can be difficult to determine which one is best. In this article, I will share what I consider to be best practices for deploying with AWS CDK. Rather than covering resource composition or Construct implementation patterns, I have specifically focused the scope on deployment-related practices in CDK . This article introduces the following four approaches: Use Static Stack Creation + Stage Synthesize once, deploy many Separate the asset build/publish and deploy phases Commit cdk.context.json Disclaimer While this article is titled "Best Practices," the content reflects my own ongoing experimentation and represents just one example . It is not nec

Best Practices for AWS CDK Deployment

In AWS CDK, you can define and deploy stacks in various ways. However, with so many approaches available, it can be difficult to determine which one is best. In this article, I will share what I consider to be best practices for deploying with AWS CDK.

Rather than covering resource composition or Construct implementation patterns, I have specifically focused the scope on deployment-related practices in CDK.

This article introduces the following four approaches:

  • Use Static Stack Creation + Stage

  • Synthesize once, deploy many

  • Separate the asset build/publish and deploy phases

  • Commit cdk.context.json

Disclaimer

While this article is titled "Best Practices," the content reflects my own ongoing experimentation and represents just one example. It is not necessarily the definitive answer, so please consider it as a reference.

Additionally, the most appropriate approach may vary depending on your team and environment. Please keep this in mind.

1. Use Static Stack Creation + Stage

The first topic is about how to define stacks. I recommend using static stack creation combined with Stage.

const app = new cdk.App();

new MyStage(app, 'Dev', { ...getProps('Dev'), env: { region: 'us-east-1', account: '111111111111', }, });

new MyStage(app, 'Stg', { ...getProps('Stg'), env: { region: 'us-east-1', account: '222222222222', }, });

new MyStage(app, 'Prod', { ...getProps('Prod'), env: { region: 'us-east-1', account: '333333333333', }, });`

Enter fullscreen mode

Exit fullscreen mode

Static Stack Creation vs Dynamic Stack Creation

There are two types of approaches to creating stacks: static and dynamic.

Static stack creation is a method where you explicitly define the stack configuration for each environment (such as dev, stg, and prod) in your code. Specifically, you create a separate stack instance with new for each environment.

const app = new cdk.App(); new MyStack(app, 'DevStack', { ... }); new MyStack(app, 'StgStack', { ... }); new MyStack(app, 'ProdStack', { ... });

Enter fullscreen mode

Exit fullscreen mode

On the other hand, dynamic stack creation is a method where you create only a single stack instance with new and pass environment information such as dev / stg / prod via the --context (-c) option of cdk deploy to generate environment-specific stacks.

const app = new cdk.App(); const env = app.node.tryGetContext('env') as string;

new MyStack(app, ${env}Stack, { ... });`

Enter fullscreen mode

Exit fullscreen mode

Why Static Stack Creation Is Recommended

There are several differences between the two approaches, but the main difference is whether all environments are synthesized at once.

The cdk deploy command internally executes a synthesis process equivalent to the cdk synth command. And the cdk synth command basically synthesizes all stacks in the entire CDK app. This means that with static stack creation, stacks for all environments such as dev / stg / prod are synthesized at once. On the other hand, with dynamic stack creation, only the stack for the environment specified via the --context option of the cdk deploy command is synthesized.

Because of this characteristic, static stack creation offers the advantage of detecting production environment errors at an earlier stage. For example, if the dev code is correct but the prod code has an error, you can detect the error before actually deploying to prod.

For this reason, I recommend static stack creation.

Cases Where Dynamic Stack Creation Is a Better Fit

If you want to avoid having prod fail because of a dev error, dynamic stack creation might be a better fit.

Also, in large-scale projects where synthesizing stacks for all environments at once takes too long, dynamic stack creation might be the better choice.

For more details about each approach, please refer to the article "CDK Environment Management: Static vs Dynamic Stack Creation", which I co-authored with Thorsten Höger, an AWS Hero from Germany.

Hybrid Approach

While I recommended static stack creation, you can also combine it with the dynamic stack creation approach using context.

This is effective, for example, when multiple people share a single AWS account for development and you want to create personal verification environments.

As shown below, by passing information to identify individuals in the stack name via the --context (-c) option of the cdk deploy command, you can dynamically generate stacks for personal environments. If personal environments are only needed for Dev, you can keep the static stack creation code as-is for Stg and Prod.

npx cdk deploy -c owner=goto Dev/*

Enter fullscreen mode

Exit fullscreen mode

const owner = app.node.tryGetContext('owner') as string; const devStageName = owner ? 
Dev-${owner} : 'Dev';

new MyStage(app, devStageName, { ...getProps('Dev'), env: { region: 'us-east-1', account: '111111111111', }, });

// Stg and Prod are the same as before // ...`

Enter fullscreen mode

Exit fullscreen mode

If each person has their own dedicated AWS environment, you can define stacks for personal environments by specifying your own AWS account ID in the account field, without using context.

const app = new cdk.App();

new MyStage(app, 'Dev', { ...getProps('Dev'), env: { region: 'us-east-1', account: process.env.AWS_ACCOUNT_ID || '111111111111', }, });`

Enter fullscreen mode

Exit fullscreen mode

Benefits of Stage

Stage is a concept positioned between App and Stack, serving as a unit for grouping multiple stacks. In applications composed of multiple stacks, using the static approach described earlier to explicitly create stack instances with new for each environment would require creating instances for the combination of all environments and stacks, resulting in verbose code.

By using Stage, you can create a Stage instance with new for each environment and define multiple stacks within it, keeping your code clean. Even if you currently have only one stack, it is a good idea to use Stage from the beginning if there is a possibility that stacks will increase in the future.

class MyStage extends cdk.Stage {  constructor(scope: cdk.App, id: string, props?: cdk.StageProps) {  super(scope, id, props);

new StackA(this, 'StackA', { ... }); new StackB(this, 'StackB', { ... }); new StackC(this, 'StackC', { ... }); } }

const app = new cdk.App(); new MyStage(app, 'Dev', { ... }); new MyStage(app, 'Stg', { ... }); new MyStage(app, 'Prod', { ... });`

Enter fullscreen mode

Exit fullscreen mode

For more details about Stage, please refer to the article "How to Use AWS CDK Stage and When to Choose Static vs Dynamic Stack Creation."

Notes on Migrating to Stage

Migrating non-Stage stacks to Stage is generally not difficult.

When using Stage, stack names follow the rule ${stageName}-${Stack ID} (e.g., MyStage-SampleStack), so simply introducing Stage will change the stack names. This means a new stack creation process will be triggered (a deletion process for the original stack will not be triggered).

However, StackProps has a property called stackName. By explicitly specifying this (setting it to the previous stack name), you can prevent the ${stageName}- prefix from being added to the stack name, allowing you to maintain the existing stack without creating a new one.

// Before migration new StackA(app, 'DevStackA', { ... }); // Stack name is 
DevStackA
 new StackB(app, 'DevStackB', { ... }); new StackC(app, 'DevStackC', { ... });

// After migration class MyStage extends cdk.Stage { constructor(scope: cdk.App, id: string, props?: cdk.StageProps) { super(scope, id, props);

// Without specifying stackName, the stack name becomes Dev-StackA, triggering new stack creation new StackA(this, 'StackA', { stackName: ${id}StackA, ... }); new StackB(this, 'StackB', { stackName: ${id}StackB, ... }); new StackC(this, 'StackC', { stackName: ${id}StackC, ... }); } }

const devStage = new MyStage(app, 'Dev');`

Enter fullscreen mode

Exit fullscreen mode

In most cases, migrating to Stage does not affect existing resources.

However, it is important to note that destructive changes can occur with some resources.

For more details, please refer to the article "AWS CDK: Resources and Causes of Replacement in Non-Stage to Stage Migration."

2. Synthesize Once, Deploy Many

This is a best practice related to CI/CD pipeline configuration.

If you are experienced in application development, you may have heard the phrase "Build once, deploy many."

In CDK, a similar concept is expressed as "Synthesize once, deploy many." This is the idea of synthesizing the CDK app only once and then deploying multiple times. In other words, the key point is to synthesize the CDK app only once within the CI/CD pipeline.

The Risk of Running Synthesis Multiple Times

In the previous section ("Why Static Stack Creation Is Recommended"), I mentioned that the cdk deploy command internally executes a synthesis process equivalent to the cdk synth command.

When you run the cdk deploy command separately for each environment such as dev / stg / prod, the cdk synth command is also executed for each environment. This means that the synthesis process runs for each environment.

When synthesis is executed multiple times, depending on the code, there is a risk that the first and second synthesis runs could produce different outputs. Even if you believe you have written correct code, unexpected mistakes can occur due to implementation errors or inconsistent versions.

Since synthesis for the development environment and synthesis for the production environment are executed at different times, unintended environmental differences may be introduced. Therefore, it is desirable to run synthesis only once.

Beyond the risk, running the synthesis process twice is also redundant and inefficient. Especially in large-scale applications, synthesis can take a considerable amount of time, potentially leading to a significant increase in deployment time.

How to Achieve This in a CI/CD Pipeline

So how do you achieve "Synthesize once, deploy many" within a CI/CD pipeline?

When you run the cdk synth command, the CDK app code is synthesized, and a set of files called the cloud assembly, including CloudFormation templates, is output to a directory called cdk.out.

On the other hand, the cdk deploy command has an --app (-a) option where you can specify the location of the synthesized cloud assembly. When specified, the synthesis process during the deploy command is skipped, and the deployment is executed based on the specified cloud assembly.

In other words, by synthesizing the CDK app only once in advance, saving the cloud assembly for all environments as an artifact, and then referencing that cloud assembly in subsequent deployment jobs for each environment, you can achieve "Synthesize once, deploy many."

This is also possible precisely because of the "static stack creation" approach mentioned earlier. With the static stack creation approach, cloud assemblies for all environments such as dev / stg / prod are generated in a single synthesis.

Specifically, using GitHub Actions as an example, the configuration would look like this:

jobs:  synth:  runs-on: ubuntu-latest  steps:

... checkout, setup-node, npm ci ...

  • run: npx cdk synth # Without specifying a stack name, same as --all
  • uses: actions/upload-artifact@v4 with: name: cdk-out path: cdk.out

deploy-dev: needs: synth steps:

... checkout, setup-node, npm ci, download-artifact, configure-aws-credentials ...

  • run: npx cdk deploy --app cdk.out --require-approval never Dev/*

deploy-stg: needs: deploy-dev steps:

...

  • run: npx cdk deploy --app cdk.out --require-approval never Stg/*

deploy-prod: needs: deploy-stg steps:

...

  • run: npx cdk deploy --app cdk.out --require-approval never Prod/*`

Enter fullscreen mode

Exit fullscreen mode

For more details about cdk.out, please refer to the article "Why is cdk.out (Cloud Assembly) Necessary in AWS CDK?."

3. Separate the Asset Build/Publish and Deploy Phases

Next is about separating the asset build/publish phase from the deploy phase in AWS CDK.

When you run the cdk deploy command, a series of processes are executed: CDK app synthesis, asset build/publish, and CloudFormation deployment.

Assets refer to things like Lambda function code defined in the CDK app and Docker images used by ECS. During cdk deploy, after CDK app synthesis is complete, Docker images are built, and then the built images and Lambda function code files are uploaded (published) to S3 or ECR.

As introduced in the previous section, using the --app (-a) option allows you to skip the synthesis process, but asset build/publish is still executed during deployment.

Having asset build/publish and deployment executed in the same command is convenient. On the other hand, from the perspective of separating build and deploy (release) as described in the Twelve-Factor App's "Build, release, run" principle, it is desirable to have the asset build/publish and deploy phases separated.

Benefits of Phase Separation

When the asset build/publish and deploy phases are separated in AWS CDK, the deployment job's responsibility becomes simpler, allowing it to focus solely on executing CloudFormation. This provides the following benefits:

  • Deployments are not affected by temporary build failures, enabling safer execution

  • When a deployment fails, you can skip the asset build and retry only the deployment

  • IAM permissions can be minimized for each phase

  • Heavy processes like Docker builds are performed in a prior phase, reducing the time spent in the deploy phase itself

  • It becomes possible to deploy using the AWS CLI with CloudFormation directly, without using the CDK CLI in the deploy phase

The cdk publish-assets Command

Previously in the CDK CLI, asset build/publish and deployment were tightly coupled.

While it was possible to perform asset build/publish using the cdk-assets library, this method was not widely adopted because it required a separate dependency from the CDK CLI and was not well documented in the CDK documentation.

To address this, the cdk publish-assets command was added to the CDK CLI, which performs synthesis through asset build/publish without deploying. By using this command, you can separate the asset build/publish and deploy phases.

NOTE: As of April 2026, the cdk publish-assets command is still in an unstable version, so you need to run it with the --unstable=publish-assets option.

# Without specifying a stack name, same as --all npx cdk publish-assets --unstable=publish-assets

You can also skip synthesis with the --app option

npx cdk publish-assets --unstable=publish-assets --app cdk.out`

Enter fullscreen mode

Exit fullscreen mode

Example of Phase Separation in a CI/CD Pipeline

By inserting a job that runs the cdk publish-assets command between the synth job and the deploy job as shown below, you can separate the asset build/publish and deploy phases.

The cdk publish-assets command can also skip synthesis by specifying the cloud assembly synthesized in the synth job via the --app option. This allows you to achieve "Synthesize once, deploy many" while also separating the asset build/publish and deploy phases.

synth:  runs-on: ubuntu-latest  steps:

... checkout, setup-node, npm ci ...

  • run: npx cdk synth
  • uses: actions/upload-artifact@v4 with: name: cdk-out path: cdk.out

publish-assets: needs: synth runs-on: ubuntu-latest steps:

... checkout, setup-node, npm ci, configure-aws-credentials ...

  • uses: actions/download-artifact@v4 with: name: cdk-out path: cdk.out
  • run: npx cdk publish-assets --unstable=publish-assets --app cdk.out --all

You can also publish for specific stages (stacks)

If AWS environments are separated, switch to a role with access to each account

npx cdk publish-assets --unstable=publish-assets --app cdk.out Dev/*

npx cdk publish-assets --unstable=publish-assets --app cdk.out Stg/*

npx cdk publish-assets --unstable=publish-assets --app cdk.out Prod/*

deploy-dev: needs: publish-assets steps:

... checkout, setup-node, npm ci, download-artifact, configure-aws-credentials ...

  • run: npx cdk deploy --app cdk.out --require-approval never Dev/*`

Enter fullscreen mode

Exit fullscreen mode

Reference

I actually contributed the cdk publish-assets command to AWS CDK! (See the Pull Request.)

The separation of the asset build/publish phase is also discussed in a blog post CDK Tips: CI/CD for CDK Applications by a former CDK maintainer. Please check it out as well.

As described in that blog post, CDK Pipelines also creates a publish-assets stage before the deploy stage.

4. Commit cdk.context.json

The last topic is about committing cdk.context.json.

What Is cdk.context.json?

cdk.context.json is a file that caches values retrieved from an AWS account during synthesis.

For example, it dynamically retrieves and stores information such as Availability Zone details and currently available Amazon Machine Image (AMI) IDs for EC2 instances from the AWS account.

Specifically, when you execute methods called context methods, such as fromLookup(), provided by CDK, the method internally retrieves information from the AWS account via the AWS SDK and stores the result in the cdk.context.json file.

const vpc = Vpc.fromLookup(this, 'Vpc', {  vpcId, });

const parameter = StringParameter.valueFromLookup(this, parameterName);`

Enter fullscreen mode

Exit fullscreen mode

Then, during synthesis or deployment, if the information already exists in the cache (cdk.context.json), the process to retrieve information from the AWS account via the SDK does not run, and the information from the file is used instead.

Benefits of Committing cdk.context.json

There are two main benefits of committing cdk.context.json:

  • Avoiding non-deterministic behavior

  • Improving deployment speed

Avoiding Non-Deterministic Behavior

For example, suppose you are deploying with a context method that retrieves the latest AMI for EC2.

If a new AMI version is released at some point, since the CDK implementation retrieves the latest image, the retrieved AMI value may differ from the one used by the already deployed EC2 instance without you realizing it, triggering an EC2 replacement (rebuild).

To avoid such "non-deterministic" behavior where the configuration changes depending on when the deployment is executed, you can cache the AMI information from the time of deployment in the cdk.context.json file. On subsequent deployments, the cached information is referenced to use the same value every time, ensuring "deterministic" behavior.

Improving Deployment Speed

In CDK apps that use context methods, if the relevant information is not found in cdk.context.json during synthesis, a dummy value is stored first and the synthesis completes.

After that, the CDK CLI retrieves the information from the AWS account via the AWS SDK and updates cdk.context.json.

Then the synthesis process runs again, and this time the final cloud assembly is generated based on the values stored in cdk.context.json.

In other words, if cdk.context.json is not committed and the file does not exist, synthesis runs twice, which wastes time.

Additionally, since AWS SDK communication runs every time synthesis or deployment is executed, even more time is wasted.

By committing cdk.context.json, synthesis only needs to run once and no AWS SDK communication is triggered, resulting in improved deployment speed.

More Details on cdk.context.json

For more details about cdk.context.json, please refer to the article "The Necessity of 'cdk.context.json' in AWS CDK."

Official Documentation

The AWS CDK official documentation also covers best practices. Please check it out as well.

Summary

In this article, I introduced the following four approaches as best practices for AWS CDK deployment:

  • Use Static Stack Creation + Stage

  • Synthesize once, deploy many

  • Separate the asset build/publish and deploy phases

  • Commit cdk.context.json

These approaches may not apply to every project. However, I hope they serve as a reference to help you choose the approach that best fits your project.

Was this article helpful?

Sign in to highlight and annotate this article

AI
Ask AI about this article
Powered by Eigenvector · full article context loaded
Ready

Conversation starters

Ask anything about this article…

Daily AI Digest

Get the top 5 AI stories delivered to your inbox every morning.

More about

releaseavailableversion

Knowledge Map

Knowledge Map
TopicsEntitiesSource
AWS CDK Dep…releaseavailableversionupdateproductapplicationDEV Communi…

Connected Articles — Knowledge Graph

This article is connected to other articles through shared AI topics and tags.

Knowledge Graph100 articles · 124 connections
Scroll to zoom · drag to pan · click to open

Discussion

Sign in to join the discussion

No comments yet — be the first to share your thoughts!

More in Releases