Terraform changed how teams manage infrastructure. Writing cloud resources in HCL, checking them into version control, and applying them through a pipeline was a genuine improvement over clicking through cloud consoles. For a lot of organizations, it still works.
The friction shows up in specific places. State files need remote storage, locking, and occasional manual surgery when things go wrong. HCL is a configuration language that doesn't compose the way application code does. Adding a database to a service means editing files in a separate repository, reviewed through a separate pipeline, by someone who may not have written the application code. And AI coding tools generate application code fluently but struggle with Terraform because HCL has less training data and configurations are highly environment-specific.
IBM's acquisition of HashiCorp and the BSL license change accelerated a search for alternatives that was already underway. The sunsetting of CDKTF in December 2025 and the deprecation of HCP Terraform's legacy free plan in March 2026 have pushed more teams to evaluate their options. But most Terraform alternatives, and most infrastructure as code alternatives in general, are still IaC with different syntax. This guide covers the full range, from IaC tools that replace HCL with real programming languages to approaches that eliminate separate infrastructure configuration entirely.
| Feature | Encore | OpenTofu | Pulumi | AWS CDK | SST | Crossplane |
|---|---|---|---|---|---|---|
| Approach | Infrastructure from code | IaC (HCL) | IaC (imperative) | IaC (constructs) | IaC (serverless-first) | IaC (Kubernetes-native) |
| Language | TypeScript / Go | HCL | TypeScript, Python, Go, C#, Java | TypeScript, Python, Go, Java, C# | TypeScript | YAML (K8s manifests) |
| State management | None (no state file) | Remote state file | Managed (Pulumi Cloud) or self-managed | CloudFormation stacks | Pulumi (managed or self-hosted) | Kubernetes etcd |
| Cloud support | AWS, GCP (provisions in your account) | AWS, GCP, Azure, 3,900+ providers | AWS, GCP, Azure, 150+ providers | AWS only | AWS only | AWS, GCP, Azure via providers |
| Infrastructure config | Zero (derived from app code) | Separate HCL files | Separate program files | Separate program files | Separate config files | Separate YAML manifests |
| Learning curve | Low (just TypeScript) | Low (same as Terraform) | Medium (new SDK per cloud) | Medium (CDK constructs) | Medium (Pulumi under the hood) | High (Kubernetes + CRDs) |
| License | Open Source (MPL-2.0) | Open Source (MPL-2.0) | Open Source (Apache 2.0) | Open Source (Apache 2.0) | Open Source (MIT) | Open Source (Apache 2.0) |
| Vendor lock-in | Low (eject to Docker anytime) | None | Moderate (Pulumi Cloud) | High (AWS only) | High (AWS only) | Moderate (Kubernetes) |
| AI agent support | MCP server, infra in same code agents read | Same as Terraform | Pulumi AI for generating IaC | Limited | Limited | None |
| Best for | Teams who want zero infra management | Existing Terraform users wanting open source | Teams who want IaC in a real language | AWS-only shops | Serverless on AWS | Kubernetes platform teams |
Encore takes a fundamentally different approach from every other tool on this list. Instead of writing infrastructure configuration in a separate file, you declare what your application needs directly in your TypeScript or Go code. Databases, Pub/Sub topics, cron jobs, object storage, and caching are declared as objects in your application. Encore's open-source tooling parses the code to understand the infrastructure requirements, and Encore Cloud provisions the corresponding AWS or GCP resources in your own account.
There's no state file because there's no separate infrastructure configuration. The application code is the source of truth. When you add a database to a service, you add a line of code in the same file where you write your queries. There's nothing to sync, no drift to detect, and no state to lock.
Pulumi and CDK let you write infrastructure in TypeScript too, but you're still maintaining a separate infrastructure program alongside your application. Encore's infrastructure declarations go in the same files as the code that uses them.
You still get configuration control over instance types, database sizes, regions, and scaling parameters through Encore Cloud, and sensible production defaults are applied automatically so you only configure what you actually want to override. For example, a Postgres database works out of the box with one line of code, and you can adjust instance sizes, regions, or scaling when you need to.
import { api } from "encore.dev/api";
import { SQLDatabase } from "encore.dev/storage/sqldb";
import { Topic } from "encore.dev/pubsub";
import { CronJob } from "encore.dev/cron";
// Provisions RDS on AWS, Cloud SQL on GCP. Runs real Postgres locally.
const db = new SQLDatabase("orders", { migrations: "./migrations" });
// Provisions SNS+SQS on AWS, GCP Pub/Sub on GCP.
const orderEvents = new Topic<OrderEvent>("order-events", {
deliveryGuarantee: "at-least-once",
});
export const createOrder = api(
{ method: "POST", path: "/orders", expose: true, auth: true },
async (req: CreateOrderRequest): Promise<Order> => {
const order = await db.queryRow`
INSERT INTO orders (customer_id, total)
VALUES (${req.customerId}, ${req.total})
RETURNING *`;
await orderEvents.publish({ orderId: order!.id, total: order!.total });
return order!;
}
);
// Provisions CloudWatch Events on AWS, Cloud Scheduler on GCP.
const cleanup = new CronJob("daily-cleanup", {
title: "Clean up expired orders",
schedule: "0 2 * * *",
endpoint: cleanupExpiredOrders,
});
That code provisions RDS, SNS+SQS, Fargate, and CloudWatch Events on AWS, or Cloud SQL, GCP Pub/Sub, Cloud Run, and Cloud Scheduler on GCP. All in your account, visible in your cloud console. The equivalent Terraform for this setup is around 300-500 lines of HCL.
The main reason is that infrastructure changes happen in the same commit as application changes. When a developer adds a Pub/Sub topic, the infrastructure is part of the pull request. There's no second repository to update, no second pipeline to run, no coordination between application and infrastructure teams. Preview environments get the right infrastructure automatically because Encore reads the code.
For teams using AI coding tools, this matters more. An AI agent writing an Encore service can see the database declaration, the topic, and the API endpoints in one file. It generates code that uses existing infrastructure correctly because the infrastructure is visible in the same context. With Terraform, the agent would need to find and interpret HCL files in a separate directory or repository.
encore build docker for standard Docker imagesAI coding tools struggle with Terraform because every resource requires dozens of configuration decisions and small mistakes cascade during apply. With Encore, an agent declares a database or a Pub/Sub topic with one line of code and the framework handles the rest with sensible production defaults. The agent doesn't need to know about instance sizes, subnet groups, or IAM policies because Encore applies those defaults automatically. If you need to override something later, you can, but the generated code is deployable from the start.
Because infrastructure lives in the same files as the application code, an agent can also see the full picture in one place: the database, the topics, the API endpoints. That's enough context to generate a new endpoint that queries an existing database or a subscriber that processes events from an existing topic.
Encore also provides an MCP server and editor rules that give agents access to database schemas, distributed traces, and the full service architecture. An agent can verify its own work against real request data and generate code that follows existing patterns.
Encore supports TypeScript and Go. It provisions infrastructure on AWS and GCP through Encore Cloud. If you need Azure support, or if your infrastructure requirements go beyond what Encore's primitives cover (custom VPC topologies, specialized AWS services), you'll still need Terraform or another IaC tool for those parts. Encore handles the common 80% of backend infrastructure. For the other 20%, it coexists with existing IaC tools rather than replacing them entirely.
Want to jump straight to a running app? Clone this starter and deploy it to your own cloud.
See the quick start guide to get started, or book a 1:1 intro for a walkthrough. For a side-by-side code comparison, see Encore vs Terraform vs CDK vs Pulumi vs SST.
OpenTofu is a fork of Terraform created in response to HashiCorp's BSL license change. It's maintained by the Linux Foundation and backed by companies including Gruntwork, Spacelift, and env0. If you're happy with how Terraform works but uncomfortable with the license, OpenTofu is the most direct replacement. Your existing .tf files, providers, and modules work without changes.
OpenTofu tracks the Terraform feature set closely while diverging in specific areas. State file encryption is built in (Terraform requires third-party tools for this). Early variable evaluation enables dynamic provider configuration that Terraform doesn't support. The provider ecosystem is fully compatible since both use the same provider protocol.
# Same HCL syntax as Terraform
resource "aws_db_instance" "orders" {
identifier = "orders"
engine = "postgres"
engine_version = "16"
instance_class = "db.t4g.micro"
db_name = "orders"
username = "app"
password = var.db_password
}
OpenTofu solves the licensing problem. It doesn't solve the state management problem, the two-codebase problem, or the HCL learning curve. If your frustration with Terraform is about the BSL license and the direction of HashiCorp under IBM, OpenTofu is the right move. If your frustration is about state files, configuration drift, or the gap between application code and infrastructure code, switching to OpenTofu won't change anything. The workflow is identical.
Pulumi replaces HCL with real programming languages. You write infrastructure in TypeScript, Python, Go, C#, or Java using Pulumi's SDK, with full IDE support, type checking, and the ability to use loops, conditionals, and abstractions from the language itself. If HCL's limited expressiveness is your primary frustration with Terraform, Pulumi addresses that directly.
Pulumi supports over 150 cloud providers and has a managed state backend (Pulumi Cloud) that handles state storage, locking, and secrets. You can also self-manage state in S3 or a local file.
import * as aws from "@pulumi/aws";
const db = new aws.rds.Instance("orders", {
engine: "postgres",
engineVersion: "16",
instanceClass: "db.t4g.micro",
dbName: "orders",
username: "app",
password: dbPassword,
});
const topic = new aws.sns.Topic("order-events");
const queue = new aws.sqs.Queue("order-processing");
new aws.sns.TopicSubscription("order-sub", {
topic: topic.arn,
protocol: "sqs",
endpoint: queue.arn,
});
Pulumi is still Infrastructure as Code. You're writing infrastructure configuration in a better language, but you're still writing infrastructure configuration. The state file exists (managed by Pulumi Cloud or self-hosted), the plan/apply cycle exists, and infrastructure lives in separate files from your application code. If you're comfortable with the IaC model and want a better language than HCL with broader cloud support, Pulumi is the strongest option in that category. If you want to eliminate separate infrastructure configuration entirely, Pulumi doesn't solve that.
AWS CDK (Cloud Development Kit) lets you define AWS infrastructure using TypeScript, Python, Go, Java, or C#. You write "constructs" that represent AWS resources, CDK synthesizes them into CloudFormation templates, and CloudFormation provisions the resources. It's AWS's answer to HCL: use a real language, get type safety and IDE support, but stay within the AWS ecosystem.
CDK has a large library of high-level constructs that bundle common patterns. A single ApplicationLoadBalancedFargateService construct, for example, creates a Fargate service, load balancer, target group, security groups, and IAM roles in one declaration.
import * as cdk from "aws-cdk-lib";
import * as rds from "aws-cdk-lib/aws-rds";
import * as ec2 from "aws-cdk-lib/aws-ec2";
const vpc = new ec2.Vpc(this, "VPC");
const db = new rds.DatabaseInstance(this, "Orders", {
engine: rds.DatabaseInstanceEngine.postgres({
version: rds.PostgresEngineVersion.VER_16,
}),
vpc,
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T4G, ec2.InstanceSize.MICRO
),
databaseName: "orders",
});
CDK is AWS only. If you deploy to GCP, Azure, or multiple clouds, CDK doesn't help. The CloudFormation layer underneath has its own set of limitations: stack size limits, slow rollbacks on failure, and occasional resources that get stuck in UPDATE_ROLLBACK_FAILED state. CDK makes the authoring experience better, but the deployment and state management experience is still CloudFormation. You can write CloudFormation templates directly in YAML or JSON without CDK, but most teams find the authoring experience too verbose without a higher-level tool on top. If you're already on AWS and want a better way to write infrastructure, CDK is mature and well-supported. If you want to avoid maintaining separate infrastructure configuration, CDK still requires that.
SST is a framework for building and deploying applications on AWS. It started as a serverless-focused tool built on CDK but has evolved into a broader platform that supports containers, static sites, and more. SST's main differentiator is its developer experience: live Lambda development with breakpoint debugging, a console for inspecting resources, and higher-level components that reduce boilerplate compared to raw CDK.
SST v3 (Ion) replaced CDK with Pulumi under the hood, giving it access to Pulumi's provider ecosystem while keeping SST's component abstractions.
// sst.config.ts
export default $config({
app(input) {
return { name: "orders", home: "aws" };
},
async run() {
const db = new sst.aws.Postgres("OrdersDB", { scaling: { min: "0.5 ACU", max: "2 ACU" } });
const api = new sst.aws.Function("API", {
handler: "src/api.handler",
link: [db],
url: true,
});
return { url: api.url };
},
});
SST is AWS-only and requires a Pulumi state backend (either Pulumi Cloud or self-managed). The move from CDK to Pulumi in v3 was a significant breaking change, and some SST v2 patterns don't carry over. SST's sweet spot is teams building on AWS Lambda and serverless infrastructure who want a better developer experience than raw CDK or Serverless Framework. If you're building container-based services or deploying to GCP, SST is less relevant. The infrastructure still lives in separate config files from your application code.
Crossplane manages cloud infrastructure through Kubernetes custom resources. You define infrastructure as Kubernetes manifests (YAML), and Crossplane controllers reconcile those manifests against your cloud provider. If your team already operates Kubernetes and wants to manage all infrastructure through kubectl and GitOps workflows, Crossplane fits into that model.
Crossplane supports AWS, GCP, and Azure through provider packages. It can also compose multiple resources into higher-level abstractions using Compositions, similar to Terraform modules or CDK constructs.
apiVersion: database.aws.crossplane.io/v1beta1
kind: RDSInstance
metadata:
name: orders
spec:
forProvider:
region: us-east-1
engine: postgres
engineVersion: "16"
instanceClass: db.t4g.micro
dbName: orders
masterUsername: app
writeConnectionSecretToRef:
name: orders-db-credentials
namespace: default
Crossplane requires a running Kubernetes cluster, which is a significant prerequisite. If you're not already running Kubernetes, adopting Crossplane means taking on Kubernetes complexity to manage your cloud infrastructure. The YAML manifests are verbose, the provider coverage varies (some AWS resources have better support than others), and debugging reconciliation failures requires understanding both Kubernetes and cloud provider APIs. Crossplane is the right choice for platform teams that have standardized on Kubernetes and want a single control plane for everything. For application developers who want to ship features without managing infrastructure, it adds a layer of complexity rather than removing one.
In 2026, teams leaving Terraform are usually frustrated by one of three things: the license, the language, or the model. The BSL license change, the CDKTF sunset, and the deprecation of HCP Terraform's legacy free plan have made this decision urgent for many organizations. The right alternative depends on which problem you're solving.
If the license is the issue and everything else works, OpenTofu is the direct answer. Same HCL, same providers, same workflow, open-source governance. Migration is a one-line change.
If HCL is the problem but the IaC model is fine, Pulumi is the strongest option. Real programming languages, broad cloud support, managed state. AWS CDK fills a similar role for teams committed to AWS. SST adds a better developer experience on top of CDK/Pulumi for serverless-heavy workloads.
If Kubernetes is your control plane for everything, Crossplane extends that model to cloud infrastructure. It makes sense for platform teams that have already bet on Kubernetes. For teams that haven't, it adds more complexity than it removes.
If the IaC model itself is the problem, with its state files, configuration drift, separate repositories, and the gap between application code and infrastructure code, those issues exist in every IaC tool on this list. OpenTofu, Pulumi, CDK, SST, and Crossplane all require you to write and maintain infrastructure configuration separately from your application, even the ones that support TypeScript.
Encore is the only tool here that eliminates that separation entirely. Infrastructure is declared in your application code, derived automatically, and provisioned in your AWS or GCP account without state files or a plan/apply cycle. For teams building TypeScript or Go backends who want to stop managing infrastructure configuration entirely, Encore is the most direct path from Terraform to shipping without DevOps overhead.
Want to jump straight to a running app? Clone this starter and deploy it to your own cloud.