Both let you define cloud infrastructure in TypeScript. They differ on what runs the deploy, where state lives, which clouds they reach and what happens when a deploy fails.
Pulumi and AWS CDK both let a TypeScript team define infrastructure in the language it already uses, with loops, functions and types instead of YAML. Underneath, CDK compiles to CloudFormation and lets AWS run the deploy, while Pulumi runs its own engine against cloud APIs. That choice decides where state lives, how failed deploys recover, and whether resources outside AWS can live in the same program.
This comparison looks at both with the same example, a queue with a dead-letter queue, and then at Encore (infrastructure from code), which takes a different route: the infrastructure an application uses is declared in the application code itself.
Each option is covered below, starting with Encore.
Encore is infrastructure from code for TypeScript and Go, with a platform that provisions and deploys into your own AWS or GCP account. Instead of a separate stack that describes a queue, the service that publishes and consumes events declares them, and Encore creates SNS topics and SQS queues on AWS or Pub/Sub on GCP, each with a dead-letter queue and IAM permissions scoped to the services that use it.

// orders/orders.ts
import { Topic, Subscription } from "encore.dev/pubsub";
import { sendReceipt } from "./email";
export interface OrderPlaced {
orderId: string;
email: string;
totalCents: number;
}
// SNS on AWS, Pub/Sub on GCP.
export const orderPlaced = new Topic<OrderPlaced>("order-placed", {
deliveryGuarantee: "at-least-once",
});
// An SQS queue with a dead-letter queue on AWS.
const _ = new Subscription(orderPlaced, "send-receipt", {
handler: async (event) => {
await sendReceipt(event.orderId, event.email);
},
retryPolicy: { maxRetries: 5, minBackoff: "10s" },
});
The topic, subscription and retry policy are declared in the service code, and encore run runs the topic and subscriber locally. Teams moving over can bring existing resources along incrementally, as described in the guides for coming from Pulumi and AWS CDK.
For coding agents, a subscription is typed application code, and the platform generates the IAM and networking it implies. Cloud access sits with Encore's IAM role, not the agent.
The infrastructure provisioning docs describe what is created in each environment.
Run in your terminal to get started locally.
AWS CDK organizes infrastructure into stacks of constructs and deploys them through CloudFormation. High-level L2 constructs set sensible defaults, so the queue with a dead-letter queue is short:
import { Stack, StackProps, Duration } from "aws-cdk-lib";
import * as sqs from "aws-cdk-lib/aws-sqs";
import { Construct } from "constructs";
export class OrdersStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const dlq = new sqs.Queue(this, "OrdersDlq", {
retentionPeriod: Duration.days(14),
});
new sqs.Queue(this, "Orders", {
visibilityTimeout: Duration.seconds(30),
deadLetterQueue: { queue: dlq, maxReceiveCount: 5 },
});
}
}
cdk deploy synthesizes a CloudFormation template and hands it to CloudFormation, which tracks state and rolls back the whole stack if any resource fails. That is CDK's strongest point: no state files to manage and a well-defined recovery path. The costs are speed, since CloudFormation deploys are slower than direct API calls, and reach, since CDK manages only AWS resources. Each account and region also needs a one-time cdk bootstrap.
Pulumi programs declare resources directly, and the engine diffs them against stored state and calls provider APIs:
import * as aws from "@pulumi/aws";
const dlq = new aws.sqs.Queue("orders-dlq", {
messageRetentionSeconds: 14 * 24 * 60 * 60,
});
export const orders = new aws.sqs.Queue("orders", {
visibilityTimeoutSeconds: 30,
redrivePolicy: dlq.arn.apply((arn) =>
JSON.stringify({ deadLetterTargetArn: arn, maxReceiveCount: 5 }),
),
});
The apply call shows Pulumi's model of outputs that are known only after deploy, and the same program can manage a Cloudflare DNS record, a Datadog monitor and an AWS queue, and pulumi preview shows the diff before pulumi up applies it. State lives in Pulumi Cloud by default or in a self-managed backend such as S3. There is no automatic rollback: a failed update leaves the resources that succeeded in place, recorded in state, and the next pulumi up continues from there.
Both tools leave the application wiring to you. The queue URL, the IAM policy that lets one service consume it, and the code that reads its messages are separate pieces that have to stay in sync across the infrastructure repository and the application.
Pick Encore when the infrastructure you manage is mostly what your application uses, such as databases, queues, buckets and cron jobs, and you want it declared in the same TypeScript or Go code, provisioned in your own AWS or GCP account and testable locally.
Pick AWS CDK when you are AWS-only and value CloudFormation's managed state and automatic rollbacks. Pick Pulumi when one program needs to manage resources across several clouds and SaaS providers, or when deploy speed and avoiding CloudFormation limits matter more. For a broader view, see our Encore vs Pulumi and Encore vs AWS CDK comparisons.
AWS CDK compiles TypeScript (or Python, Java, C# and Go) into CloudFormation templates, and CloudFormation performs the deploy, keeps the state and rolls back failed updates. It targets AWS only. Pulumi runs its own engine that calls cloud provider APIs directly, keeps state in Pulumi Cloud or a backend you choose, and supports AWS, GCP, Azure, Kubernetes and many other providers.
CDK is a natural default when everything is on AWS and you want CloudFormation's managed state and automatic rollbacks at no extra cost. Pulumi fits better when you also manage resources outside AWS, such as Cloudflare, Datadog or GCP, or want faster deploys without CloudFormation's limits.
For the resources your application uses directly, yes. With Encore, you declare them in application code, for example new Topic<OrderPlaced>("order-placed", { deliveryGuarantee: "at-least-once" }) and new Subscription(orderPlaced, "send-receipt", { handler }), and Encore provisions SNS and SQS on AWS, or Pub/Sub on GCP, with IAM and dead-letter queues. Shared resources outside the application can stay in Pulumi or CDK.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.