// Stay in touch?
Products
Encore CloudEncore Cloud
Encore.tsEncore.ts
Encore.goEncore.go
PricingPricing
Book a DemoBook a Demo
Use Cases
AI-Powered DevelopmentAI-Powered Development
Event-Driven SystemsEvent-Driven Systems
Distributed SystemsDistributed Systems
Case StudiesCase Studies
ShowcaseShowcase
Resources
DocsDocs
InstallInstall
Example AppsExample Apps
Demo videoDemo video
ArticlesArticles
GitHub ReleasesGitHub Releases
Systems Operational
Company
About UsAbout Us
Swag ShopSwag Shop
ContactContact
JobsJobs
PressPress
TermsTerms
Privacy PolicyPrivacy Policy
Data Processing AgreementData Processing Agreement
Enterprise SLAEnterprise SLA
Encore
© 2026 EncoreAll rights reserved
© 2026 Encore All Rights Reserved
GitHubDiscordYouTube

Pulumi vs OpenTofu in 2026

A real programming language or an open-source, Terraform-compatible HCL engine

07/08/26
9 Min Read
Ivan Cernja
07/08/26

Pulumi vs OpenTofu in 2026

A real programming language or an open-source, Terraform-compatible HCL engine

Ivan Cernja
9 Min Read

Both tools solve the same problem: describe cloud infrastructure once, then create, update, and destroy it predictably. The choice between them comes down to one question you answer before writing a line of config. Do you want to describe infrastructure in a general-purpose programming language you already know, or in HCL, the declarative configuration language that Terraform popularized and that OpenTofu now carries forward under an open-source license?

Pulumi takes the first path. You write TypeScript, Python, Go, or C#, and Pulumi's engine turns your program's output into a resource graph. OpenTofu takes the second. It is the Linux Foundation fork of Terraform, created after HashiCorp relicensed Terraform under the Business Source License in 2023, and it stays deliberately close to Terraform's HCL, state format, and provider protocol.

This guide compares the two on the axes that decide the pick: language model, state handling, provider ecosystem, and licensing. It shows the same resource in both, and it ends with guidance on when each is the right call.

Feature Comparison

PulumiOpenTofu
LanguageTypeScript, Python, Go, C#, Java, YAMLHCL
ModelImperative program that builds a declarative graphDeclarative config
LicenseApache 2.0 (CLI + SDKs)Mozilla Public License 2.0
StewardshipPulumi CorporationLinux Foundation
Default state backendPulumi Cloud (SaaS, free individual tier)Configured backend (S3, GCS, Azure Blob, etc.)
Self-managed stateYes (S3, GCS, Azure Blob, local)Yes (native)
Provider ecosystemNative Pulumi providers + bridged Terraform providersTerraform provider protocol (near-full compatibility)
Terraform compatibilityConvert via pulumi convert --from terraform, adopt via pulumi importDrop-in for most Terraform 1.5 workflows
TestingNative unit/integration tests in the host languageterraform test / third-party (Terratest, etc.)
State encryptionSecrets encrypted by defaultNative state encryption since the fork

The Same Resource in Pulumi and OpenTofu

Here is an AWS S3 bucket with versioning and a tag, written in Pulumi (TypeScript) and in OpenTofu (HCL).

Pulumi (TypeScript)

import * as aws from "@pulumi/aws"; const bucket = new aws.s3.Bucket("assets", { tags: { Environment: "production" }, }); new aws.s3.BucketVersioning("assets-versioning", { bucket: bucket.id, versioningConfiguration: { status: "Enabled" }, }); export const bucketName = bucket.id;

OpenTofu (HCL)

resource "aws_s3_bucket" "assets" { tags = { Environment = "production" } } resource "aws_s3_bucket_versioning" "assets" { bucket = aws_s3_bucket.assets.id versioning_configuration { status = "Enabled" } } output "bucket_name" { value = aws_s3_bucket.assets.id }

For a single bucket the two are close in length and clarity. HCL is arguably tighter here, and that is HCL's strength: a flat resource declaration with no surrounding program.

The difference shows when logic enters. Suppose you need a bucket per team, with a policy that varies by team tier. In Pulumi you write a normal loop and a normal conditional:

import * as aws from "@pulumi/aws"; const teams = [ { name: "payments", tier: "premium" }, { name: "marketing", tier: "standard" }, ]; for (const team of teams) { const bucket = new aws.s3.Bucket(`${team.name}-assets`, { tags: { Team: team.name, Tier: team.tier }, }); if (team.tier === "premium") { new aws.s3.BucketLifecycleConfiguration(`${team.name}-lifecycle`, { bucket: bucket.id, rules: [{ id: "archive", status: "Enabled", /* ... */ }], }); } }

In HCL you reach for for_each, count, and conditional expressions:

variable "teams" { default = { payments = "premium" marketing = "standard" } } resource "aws_s3_bucket" "assets" { for_each = var.teams tags = { Team = each.key Tier = each.value } } resource "aws_s3_bucket_lifecycle_configuration" "assets" { for_each = { for k, v in var.teams : k => v if v == "premium" } bucket = aws_s3_bucket.assets[each.key].id # rule { ... } }

Both work. The HCL version is a common source of friction because for_each, count, and ternary expressions are a constrained sub-language, and once the logic gets involved you hit the limits of what HCL will express cleanly. The Pulumi version is ordinary code, testable with the language's own tools, but it also means an infrastructure change now inherits everything that comes with a full programming language: runtime bugs, dependency upgrades, and behavior that varies between runs, all of which HCL's constraints were designed to keep out.

State Handling

Neither tool escapes state. Both keep a state file that records the mapping between the resources you declared and the real resources in your cloud account, and both diff that state against reality on every plan.

OpenTofu stores state in a configured backend. In practice that is an S3 bucket with DynamoDB locking, a GCS bucket, Azure Blob, or a third-party runner like Spacelift or Scalr. You choose it, you own it, and you handle backup and access control. OpenTofu added native state encryption after the fork, so state at rest can be encrypted without a separate KMS-wrapped backend workflow.

Pulumi defaults to Pulumi Cloud, a hosted backend that stores state and provides history, RBAC, and a web console. It is free for individuals and paid for teams. If you would rather not depend on Pulumi's SaaS, you point the CLI at an S3, GCS, Azure Blob, or local backend with pulumi login s3://my-bucket, and you run entirely self-managed. Pulumi encrypts secret values inside state by default, using either a Pulumi Cloud key or a provider KMS you configure.

The practical difference: OpenTofu assumes you bring your own backend from day one, and Pulumi assumes you use its cloud unless you opt out. Both let you self-host. If your compliance posture forbids state leaving your accounts, both can satisfy it, but with Pulumi you have to actively choose the self-managed path.

Providers and Ecosystem

This is where OpenTofu's compatibility bet pays off. OpenTofu speaks the same provider protocol as Terraform, so the thousands of existing Terraform providers run on it with no changes, and OpenTofu maintains its own registry alongside the Terraform Registry. If a provider exists for your service, OpenTofu almost certainly runs it.

Pulumi has native providers for the major clouds (AWS, Azure, GCP, Kubernetes) that are generated from the cloud APIs, plus a bridge that wraps Terraform providers so Pulumi can use them too. Coverage is broad. The bridged providers occasionally lag the native Terraform version by a release, and the ergonomics of a bridged provider are slightly less polished than a first-class Pulumi one, but for common infrastructure you are unlikely to hit a wall.

For teams with an existing Terraform or OpenTofu estate, the migration story matters:

  • Staying in the HCL world (Terraform to OpenTofu) is close to a no-op. Same HCL, same state format, run tofu init against existing state and confirm a clean plan.
  • Moving to Pulumi means converting HCL to your chosen language. pulumi convert --from terraform automates most of it (Pulumi reports 90 to 95 percent of typical programs converting without manual TODOs), and pulumi import can adopt existing resources into Pulumi state. It is a genuine conversion rather than a rename, and complex modules need manual review afterward.

Licensing and Governance

Both are more open than post-2023 Terraform, but for different reasons.

  • OpenTofu is MPL 2.0, an OSI-approved open-source license, governed by the Linux Foundation with a public roadmap and multiple corporate contributors. Its entire reason to exist is license permissiveness after HashiCorp's BSL relicense. For a deeper look at that split, see OpenTofu vs Terraform in 2026.
  • Pulumi's CLI and SDKs are Apache 2.0, also OSI-approved. The company behind it, Pulumi Corporation, monetizes through Pulumi Cloud rather than by relicensing the engine. Governance is single-vendor, which is a different risk profile from a foundation, though the permissive engine license means a fork is legally possible if the relationship ever soured.

If your organization has a hard policy requiring OSI-approved licenses, both pass. If you specifically want foundation governance rather than a single commercial steward, OpenTofu is the closer fit. If you want a permissive engine license and are comfortable with a vendor whose business model is the hosted backend, Pulumi fits.

When to Choose Each

Choose OpenTofu if:

  • You already have Terraform HCL and want an open-source engine with a near-zero-cost migration.
  • Your team, CI vendor, or policy platform has standardized on the Terraform/OpenTofu ecosystem.
  • You want the widest possible provider compatibility with the least surprise.
  • You prefer declarative config that intentionally cannot run arbitrary code.
  • Foundation governance and an OSI license are procurement requirements.

Choose Pulumi if:

  • Your infrastructure has real logic (loops, conditionals, computed values) that HCL expresses awkwardly.
  • Your team already writes TypeScript, Python, Go, or C# and wants to reuse the language, its IDE support, and its test frameworks for infrastructure.
  • You want to unit-test infrastructure code with the same tooling as your application.
  • You value the hosted Pulumi Cloud backend and are comfortable with a single-vendor tool.

HCL's constraints help when your infrastructure is mostly static declarations and get in the way when it is not. Pulumi's general-purpose languages help when you need real logic and cost you when a stray abstraction turns a one-line change into a debugging session. The deciding factor is how dynamic your infrastructure is, well ahead of which language you find more pleasant to read.

How Encore Compares

Both Pulumi and OpenTofu share a shape: infrastructure lives in a separate codebase from the application, tracked in a separate state file, applied by a separate tool. Your application code has no awareness of the database or queue it depends on, and the infrastructure definition has no awareness of how the application uses them. You keep the two in sync by hand.

For teams that would rather not manage state or a parallel infrastructure repository at all, a different model is worth knowing about. Encore is an open-source backend framework (around 12,000 GitHub stars, used in production by teams including Groupon) where you declare infrastructure directly in your application code, and Encore Cloud provisions the real resources in your own AWS or GCP account. There is no HCL file, no Pulumi program, and no state file to store or lock.

import { api } from "encore.dev/api"; import { SQLDatabase } from "encore.dev/storage/sqldb"; import { Topic } from "encore.dev/pubsub"; // Provisions managed Postgres (RDS on AWS, Cloud SQL on GCP, Docker locally). const db = new SQLDatabase("orders", { migrations: "./migrations" }); // Provisions Pub/Sub (SNS+SQS on AWS, Pub/Sub on GCP). export const orderPlaced = new Topic<{ orderID: string }>("order-placed", { deliveryGuarantee: "at-least-once", }); export const create = api( { method: "POST", path: "/orders", expose: true }, async (req: { customerID: string }) => { const row = await db.queryRow` INSERT INTO orders (customer_id) VALUES (${req.customerID}) RETURNING id `; await orderPlaced.publish({ orderID: row.id }); return row; }, );

This does not replace Pulumi or OpenTofu for every job. Teams managing large VM fleets, complex networking, or IAM at scale will keep using an IaC tool, and Encore itself has an encore build docker path that produces a standalone image for teams that want to leave. What it replaces is the configuration sprawl and state management that grow around an application-level backend. If your infrastructure is mostly the databases, queues, cron jobs, and storage your services reach for, defining it in the application code removes the second codebase entirely.

Deploy with Encore

Skip the separate IaC codebase and state file. Deploy a TypeScript backend with Postgres and Pub/Sub to your own AWS or GCP account in minutes.

Deploy

Related Resources

  • OpenTofu vs Terraform in 2026
  • Terraform vs Pulumi
  • Migrating from Terraform to Pulumi
  • Pulumi alternatives
  • Encore vs Terraform, CDK, Pulumi, and SST

Frequently asked questions

Is OpenTofu compatible with Terraform providers?

Yes. OpenTofu uses the same provider protocol as Terraform, so the vast majority of Terraform providers run unchanged. OpenTofu also runs its own registry alongside the Terraform Registry.

Can Pulumi reuse Terraform providers?

Yes. Pulumi bridges many Terraform providers, and it can convert existing HCL with `pulumi convert --from terraform` and adopt live resources into Pulumi state via `pulumi import`. Coverage is broad but not identical to the native Terraform ecosystem.

Is Pulumi open source?

The Pulumi CLI and SDKs are Apache 2.0. Pulumi Cloud, the default state and secrets backend, is a paid SaaS with a free individual tier. You can self-manage state in S3, GCS, or Azure Blob instead.

Why did OpenTofu fork from Terraform?

HashiCorp relicensed Terraform under the Business Source License in August 2023. A group of vendors and community members forked the last MPL-licensed release into OpenTofu, now hosted under the Linux Foundation.

Does Pulumi or OpenTofu handle state for you?

Neither removes state. Both track a state file that maps declared resources to real cloud resources. Pulumi defaults to Pulumi Cloud for storage; OpenTofu uses a configured backend such as S3. You still manage locking, backups, and access.

Which is faster to learn for an application developer?

Pulumi, if you already write TypeScript, Python, Go, or C#, because you reuse the language, its tooling, and its testing frameworks. OpenTofu's HCL is a smaller surface but a new language to learn.

Contents
Feature Comparison
The Same Resource in Pulumi and OpenTofu
Pulumi (TypeScript)
OpenTofu (HCL)
State Handling
Providers and Ecosystem
Licensing and Governance
When to Choose Each
How Encore Compares
Related Resources
Encore

The backend platform for humans and agents

Build backends in Go or TypeScript and run them on your own AWS or GCP, with the guardrails your team and AI agents need to ship safely.

Ready to build your next backend?

Encore is the Open Source framework for building robust type-safe distributed systems with declarative infrastructure.