Pulumi made infrastructure a real program, with its own state and deploys. These are the best alternatives in 2026, now that agents write most of the code.
Pulumi was built to give infrastructure the same tools as the rest of your code, a real programming language with types, loops, and reusable abstractions instead of a configuration language of its own, and that was worth its cost while a person wrote each program and reviewed each deploy. So what happens when you bring agents into the mix? Recent research into AI-native delivery shows they have increased code output far faster than teams have increased how often they ship, while the infrastructure still lives in a separate program behind its own state and its own deploy. Pulumi's own agent, Neo, automates that deploy, proposing changes as pull requests and moving them through preview and approval, but the change still only becomes real once it applies, and there is no way to stand the infrastructure up and check it in the dev loop first. This is what this new bottleneck looks like now:
In practice, bringing AI into the development cycle does raise code and feature output, but those gains keep running into the infrastructure problems underneath. They tend to show up as some, if not all, of these:
| Alternative | Approach | Best fit |
|---|---|---|
| Encore | Infrastructure declared as typed primitives inside the application code | Engineering teams standardizing on AI agents to write application and infrastructure code |
| Terraform | Separate HCL configuration with its own state and plan-and-apply workflow | Teams that want the largest provider ecosystem and are comfortable in HCL |
| OpenTofu | The open-source fork of Terraform, same HCL and workflow | Teams that want the Terraform model without the source-available license |
| AWS CDK | A separate infrastructure program compiled to CloudFormation | AWS-only teams that want a real language tied to CloudFormation |
| SST | A separate config file built on Pulumi, tuned for app developers | TypeScript teams building serverless-heavy AWS apps that want less boilerplate |
| Crossplane | Infrastructure as Kubernetes resources reconciled by a controller | Kubernetes platform teams that want to expose a curated internal API |
Encore is a platform for automating infrastructure from local development to cloud deployment. Instead of describing application architecture in a separate infrastructure program, you declare services, APIs, and infrastructure resources in application code. Encore uses those declarations to run the application locally, validate it, and provision the infrastructure it needs in your own AWS or GCP account, without giving up control over how each environment is set up.
The difference is easiest to see on a single resource. In Pulumi, a Postgres database is a typed object in a program with its own state; in Encore, the application just declares what it needs:
Pulumi
const db = new aws.rds.Instance("app-db", {
engine: "postgres",
instanceClass: "db.t3.micro",
allocatedStorage: 20,
dbName: "app",
password: config.requireSecret("dbPassword"),
skipFinalSnapshot: true,
// ... plus the stack, state, and pulumi up
});
Encore
const db = new SQLDatabase("orders", {
migrations: "./migrations",
});
The same Postgres database, written as a Pulumi program and declared in Encore.
At first it looks like a trade-off between configurability and simplicity, but in reality, it's the same level of control, just each in its own place. Encore, through static analysis (a fancy way of saying it reads your code without running it), infers what it needs from the declaration: the database, its migrations, which services use it, and the IAM that follows from that. How it runs, such as its instance size, storage, networking, and backups, is abstracted into your environment configuration, which stays outside the code you (or an agent) write.
encore run. The same declaration provisions the real bucket in production, so what runs locally matches what ships.A Pub/Sub topic, an object storage bucket, or a cron job (and the other primitives Encore supports) works the same way. When an agent adds one:
encore run, the change runs against real infrastructure locally and in each preview environment, so an agent (or human) can verify how it behaves end to end before it reaches production.Encore is the strongest fit for teams that want to get the most out of AI agents without loosening the guardrails around production, building backend services on AWS or GCP where infrastructure has become the delivery bottleneck.
Adopting it also assumes your existing Pulumi program can stay in place, still managing the shared, org-level, or unsupported infrastructure it already handles while Encore takes the application resources it supports. Encore connects to databases you already run, such as RDS and Cloud SQL, so the two run side by side.
Terraform is the tool Pulumi set out to improve on, and it remains the default infrastructure as code, with the largest provider ecosystem of any option here. You describe resources in HCL, a configuration language of its own, and Terraform plans and applies them against a state file it maintains. The same Postgres database is a block of HCL:
resource "aws_db_instance" "users" {
identifier = "users-db"
engine = "postgres"
instance_class = "db.t3.micro"
allocated_storage = 20
db_name = "users"
username = var.db_username
password = var.db_password
# ... plus networking, IAM, secrets
}
An agent generates HCL as fluently as it generates a Pulumi program, but the change stays inert until someone runs terraform plan and terraform apply, and it runs against shared state a careless edit can corrupt. There is no way to stand the real infrastructure up locally and check the change in the dev loop, so the apply, usually against a live environment, is the first real test. Moving from Pulumi to Terraform trades a real language for a bigger ecosystem, but the separate-program-and-state workflow is the same.
Terraform fits teams that want the broadest provider coverage and the largest ecosystem of modules and examples, and are comfortable staying in HCL rather than a general-purpose language. It is source-available under the BSL license now rather than open source, which is what led to the OpenTofu fork, and the workflow is unchanged from what you have in Pulumi: a separate configuration behind its own state and its own apply.
OpenTofu is a fork of Terraform, created after HashiCorp moved Terraform to the source-available BSL license in 2023, and now maintained by the Linux Foundation under the open source MPL 2.0. It keeps HCL, the provider protocol, modules, and the plan-and-apply workflow, so existing .tf files, providers, and state work with little or no change. The same database is the same .tf:
# The same .tf as Terraform, unchanged: providers and state carry over
resource "aws_db_instance" "users" {
identifier = "users-db"
engine = "postgres"
instance_class = "db.t3.micro"
# ... plus storage, credentials, networking, IAM
}
An agent works with OpenTofu just as it would with Terraform. It can generate HCL fluently, but the change stays inert until someone runs tofu plan and tofu apply, and it runs against the same shared state a careless edit can corrupt. There is no way to stand the real infrastructure up locally and check the change in the dev loop, so the apply itself, usually against a live environment, is the first real test. The split between application code and a stateful infrastructure pipeline is structural, and an open source license does not close it.
OpenTofu is the least disruptive move for teams that want the Terraform model without its source-available license, and it has since added features of its own, such as built-in state encryption and OCI-based provider registries. Like Terraform, it keeps HCL and state, so an application change can still require a separate infrastructure change.
AWS CDK lets you define AWS infrastructure in TypeScript, Python, Java, C#, or Go, using constructs that bundle several resources into reusable components. Like Pulumi, it gives you a real language, but it synthesizes that code into a CloudFormation template, which AWS then deploys and tracks as a stack.
const db = new rds.DatabaseInstance(this, "Postgres", {
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_16,
}),
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.MICRO),
credentials: rds.Credentials.fromGeneratedSecret("postgres"),
vpc,
});
Constructs are ordinary code, so an agent authors them fluently, but the code is only the first phase. It has to be synthesized to CloudFormation and deployed with real credentials before anything exists, and the agent has to reason about a two-step path, source to template to stack, that it cannot see from the source alone. There is no local run against real infrastructure to validate the change first.
CDK fits teams committed to AWS who want a real language, like Pulumi, but tied to CloudFormation and its stacks rather than a separate state backend. It stays inside AWS, though, with infrastructure in a separate app and no path to GCP or Azure, so it is a narrower choice than the multi-cloud program Pulumi gives you. Its first major version reached end of support back in 2023, but version 2 ships weekly and is actively maintained, so the reason to weigh it against Pulumi is the AWS lock-in, not any lack of upkeep.
SST is a framework for building and deploying full-stack applications, mostly on AWS, with higher-level components, resource linking, and a console for inspecting what you shipped. It actually runs on Pulumi with Terraform providers underneath, which is what lets it reach past AWS and past CloudFormation's stack limits, but it wraps that in an app-developer experience.
// sst.config.ts
const vpc = new sst.aws.Vpc("MyVpc");
const db = new sst.aws.Postgres("MyDatabase", { vpc });
Infrastructure and application code live in one TypeScript project, so an agent editing an SST app touches both at once, and the components are declarative and easy to generate. Nothing provisions until sst deploy runs, though, and because it sits on Pulumi, the same state and drift concerns apply, so the change still passes through a separate deploy before it is real. There is no local run against real infrastructure to check it in the dev loop first.
SST fits TypeScript teams building serverless-heavy applications on AWS that want less infrastructure boilerplate and a tighter link between app and cloud. It is the closest of these tools to putting infrastructure beside application code, but it keeps a separate sst.config.ts, the Pulumi state backend underneath, and a deploy step, and it stays oriented around AWS.
Crossplane turns a Kubernetes cluster into a control plane for cloud infrastructure. You describe resources as Kubernetes objects, and Crossplane provisions and continuously reconciles them against AWS, GCP, or Azure, while Compositions let a platform team expose its own higher-level APIs to developers. Its second major version, which graduated within the CNCF in 2025, made those resources namespaced and lets Compositions build on any Kubernetes resource, not only Crossplane's own.
apiVersion: rds.aws.m.upbound.io/v1beta1
kind: Instance
metadata:
name: app-postgres
spec:
forProvider:
region: us-east-1
engine: postgres
instanceClass: db.t3.micro
allocatedStorage: 20
A managed resource is just YAML, which an agent generates easily, but it does nothing until it reaches a cluster whose control plane already has the matching CRDs and a credentialed provider installed. There is no local run or preview without that machinery, and provider field names drift between versions, so a mismatch only surfaces at apply and reconcile time.
Crossplane makes sense when Kubernetes is already your control plane and the goal is to hand developers a curated internal API, which is a platform engineering decision more than a simpler way to replace Pulumi. Teams that do not already run Kubernetes take on the largest prerequisite of any option here, a cluster, provider packages, Compositions, and the reconciliation failures that come with them, all around the cloud resources themselves.
The right choice comes down to why you are leaving Pulumi:
The five that keep a separate program or configuration keep its state and its apply along with it, so an agent can author the change but not make it real on its own. Encore is the one that folds that step into the application code, and it coexists with your existing Pulumi setup for anything it does not cover. The quickstart is the fastest way to see the difference.
It depends on why you are leaving Pulumi: Terraform or OpenTofu for the biggest ecosystem and HCL, AWS CDK to stay on AWS with a real language, SST for serverless apps on AWS, Crossplane for teams already on Kubernetes, and Encore for teams adopting AI who want infrastructure to ship with the application code.
Agents can author configuration in any of them, since HCL, TypeScript, and YAML are all easy to generate. The difference is what happens next: with most, the change still waits on a separate plan, apply, or deploy, while tools built around agents provision infrastructure directly from the code the agent writes.
Mostly no. Terraform, OpenTofu, AWS CDK, SST, and Crossplane each keep a separate program or configuration and a state store of some kind. Encore derives its model from your code, so there is no separate infrastructure state file to maintain.
No. The code declares what the application depends on, while environment settings like capacity, networking, and backups stay in separate configuration. It is the same control, just kept in two places.
Yes. Encore is built to coexist, taking the infrastructure it supports while your existing Pulumi program keeps managing the rest, and it can connect to databases you already run such as RDS and Cloud SQL.
Ship without waiting on Terraform
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.