
September 09, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Pulumi: IaC in Real Programming Languages is the idea that cloud infrastructure should be written in languages your team already uses—TypeScript, Python, Go, C#, Java, YAML—not a bespoke DSL like HCL. Most teams I work with already run GitLab CI/CD for PHP projects and Deployer 7 on Ubuntu. They want the same loops, functions, and type checking in infrastructure code. Pulumi delivers that through a general-purpose engine that calls cloud APIs and tracks state, while you stay in familiar syntax. This guide covers how it works, how it compares to Terraform, and how to ship it safely in production.
What is Pulumi and how does IaC in real programming languages work?
Pulumi is an open-source infrastructure-as-code platform. You write a program in a supported language. The Pulumi CLI runs that program, which declares desired resources through provider SDKs. Pulumi diffs the result against stored state and applies create, update, or delete operations to your cloud account.
The mental model differs from Terraform. Terraform reads static .tf files. Pulumi runs code. That means you can use for loops, conditionals, functions, classes, and package imports—the same tools you use in application code. A law-firm portal stack and a Laravel API stack can share a TypeScript module for standard VPC layout without copy-pasting HCL blocks.
Pulumi supports two programming models:
- Declarative resources — You call
new aws.s3.Bucket()or equivalent. Pulumi tracks dependencies automatically. - Component resources — You wrap repeated patterns into reusable classes, like a private Laravel-ready EC2 module with security groups baked in.
State lives in a backend you choose: Pulumi Cloud (free tier available), self-hosted object storage, or local files for experiments. Secrets can be encrypted with a passphrase or a cloud KMS key. For teams already scanning IaC in CI, pair Pulumi with tools covered in our IaC security scan guide.
Official docs at pulumi.com/docs/concepts/how-pulumi-works describe the engine in detail. The key takeaway: your language runtime executes once per preview or update. Pulumi records outputs and builds a dependency graph from resource registrations, not from parsing a config file.
How does Pulumi compare to Terraform and CloudFormation?
Teams evaluating Pulumi usually already know Terraform or AWS CloudFormation. The comparison is not about raw provider coverage—both Pulumi and Terraform reach most major clouds. The difference is developer experience, abstraction power, and operational fit.
| Criterion | Pulumi (real languages) | Terraform (HCL) | CloudFormation (YAML/JSON) |
|---|---|---|---|
| Language | TypeScript, Python, Go, C#, Java, YAML | HCL + limited expressions | YAML or JSON templates |
| Abstraction | Functions, classes, npm/PyPI packages | Modules; logic via HCL functions | Nested stacks, macros |
| Testing | Unit tests with mocks (Jest, pytest) | Terraform test (HCL), terratest (Go) | TaskCat, manual drift checks |
| IDE support | Full autocomplete, jump-to-def, refactor | Good with HCL extension | Basic schema validation |
| State | Pulumi Service, S3, local | Remote backend (S3, Terraform Cloud) | Managed by AWS |
| Multi-cloud | Strong | Strong | AWS only |
| Learning curve | Low if team knows the language | Medium—new DSL | Medium—verbose templates |
Terraform remains the default in many enterprises. HCL is readable in pull requests and the ecosystem is enormous. Pulumi wins when your team is already TypeScript-heavy or when infrastructure needs real programmatic logic—dynamic subnet counts, environment-specific branching, or shared libraries published to a private registry.
On sister sites I maintain with Deployer 7 and GitLab CI, infrastructure changes are small and infrequent. Terraform or even hand-provisioned EC2 plus Ansible often suffices. Pulumi pays off when you spin up isolated preview environments per branch or manage dozens of similar client stacks from one codebase. See our Notary Kathmandu deployment portfolio for the simpler end of that spectrum.
For container and IaC scanning, both tools output plans you can gate in CI. Our Trivy scan guide applies regardless of which IaC engine you pick.
How do you write your first Pulumi program in TypeScript or Python?
Start with a single stack—Pulumi's name for one deployable unit tied to one state file. One stack might be production. Another might be staging. Each stack gets its own config values.
Install the CLI and create a project
Install Pulumi from pulumi.com/docs/install. You need Node.js 26 LTS for TypeScript or Python 3.x for Python examples below.
- Run
pulumi login— use Pulumi Cloud or configure an S3 backend. - Create a project:
mkdir pulumi-demo && cd pulumi-demo && pulumi new aws-typescript - Select defaults, then run
pulumi upto preview and apply. - Destroy when finished:
pulumi destroy.
TypeScript example: S3 bucket with tags
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const env = config.require("environment");
const bucket = new aws.s3.Bucket("app-assets", {
tags: {
Environment: env,
ManagedBy: "pulumi",
},
});
export const bucketName = bucket.id; TypeScript gives you compile-time checks. Misspell aws.s3.Buckt and the build fails before CI runs. That alone saves hours on typo-driven failed applies.
Python example: loop over availability zones
import pulumi
import pulumi_aws as aws
config = pulumi.Config()
region = config.get("region") or "ap-southeast-1"
vpc = aws.ec2.Vpc("main-vpc", cidr_block="10.0.0.0/16")
for i, az in enumerate(["ap-southeast-1a", "ap-southeast-1b"]):
aws.ec2.Subnet(
f"subnet-{i}",
vpc_id=vpc.id,
cidr_block=f"10.0.{i}.0/24",
availability_zone=az,
) A for loop in HCL requires count or for_each with extra ceremony. In Python it reads like application code. Validate JSON configs before apply using a JSON formatter and linter workflow in CI if you load external data files.
Component resources for reuse
Extract repeated infrastructure into a class. A LaravelSiteComponent might provision RDS, ElastiCache Redis 8.10, an ALB, and an EC2 autoscaling group with user-data that installs PHP 8.5 and Nginx. Application teams import it like any npm package:
import { LaravelSite } from "@myorg/pulumi-laravel";
const site = new LaravelSite("court-portal", {
domain: "example.com",
phpVersion: "8.5",
dbEngine: "mysql",
}); That pattern mirrors how I structure Deployer recipes across legal-tech sister sites—shared base, per-site overrides. Pulumi just moves the sharing into a typed library instead of copy-pasted shell scripts.
How do you integrate Pulumi into CI/CD pipelines?
Production Pulumi never runs from a laptop. It runs in CI with locked credentials and policy gates. The flow matches Terraform: preview on pull request, apply on merge to main.
GitLab CI example
stages:
- preview
- deploy
pulumi:preview:
stage: preview
image: pulumi/pulumi-nodejs:latest
script:
- npm ci
- pulumi stack select staging
- pulumi preview --non-interactive
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
pulumi:deploy:
stage: deploy
image: pulumi/pulumi-nodejs:latest
script:
- npm ci
- pulumi stack select production
- pulumi up --yes --non-interactive
rules:
- if: $CI_COMMIT_BRANCH == "main" Store PULUMI_ACCESS_TOKEN and cloud credentials in GitLab CI variables. Never commit secrets. Use Pulumi's built-in secret encryption for database passwords and API keys. Rotate tokens the same way you rotate Deployer SSH keys.
For PHP-centric teams, this sits alongside existing pipelines described in our Pulumi infrastructure as code overview. Application deploy stays Deployer; infrastructure deploy stays Pulumi. Clear boundary, fewer midnight surprises.
Multi-cloud or multi-region setups add complexity. Read our active-active vs active-passive multi-cloud guide before designing failover in code. Pulumi can express either pattern, but code cannot fix a wrong architecture choice.
When should you choose Pulumi over HCL-based IaC tools?
Not every project needs Pulumi. Use this decision lens before you adopt a new toolchain.
- Choose Pulumi when your team already writes TypeScript or Python daily, you need shared libraries across dozens of stacks, or you want unit tests beside infrastructure code.
- Stay on Terraform when ops staff prefer HCL diffs in PRs, you rely on a mature module registry, or regulatory auditors expect
.tfartifacts. - Skip both initially when you run one or two EC2 instances with Deployer and manual provisioning—common on budget-sensitive Nepal client projects.
I've seen startups adopt Pulumi because the founding team was JavaScript-native, then struggle when the only person who understood the stack left. Treat Pulumi code like application code: README, code review standards, and onboarding docs. Our Linux system administration service often starts with auditing whether IaC is even warranted yet.
For larger custom platforms—marketplaces, multi-tenant SaaS, automated client provisioning—Pulumi fits naturally inside enterprise application development workflows. The same team that ships Laravel 13.x can own the VPC that hosts it.
What are common Pulumi mistakes in production?
Pulumi removes HCL friction but introduces familiar software pitfalls. Watch for these on real deployments.
Side effects outside resource declarations
Your program must only create side effects through Pulumi resources. Do not call fetch() to hit live APIs during every preview unless outputs are wrapped in resources or cached. Unexpected network calls slow CI and cause flaky plans.
Secrets in plain stack config
Never put passwords in Pulumi.prod.yaml without encryption. Use pulumi config set --secret dbPassword. Pair with AWS Secrets Manager or Parameter Store for runtime secrets consumed by Laravel apps.
Monolithic stacks
One stack for everything creates long apply times and risky blast radius. Split by lifecycle: network stack, data stack, app stack. Use StackReference to read outputs across stacks—the Pulumi equivalent of Terraform remote state data sources.
Skipping drift detection
Console clicks still happen. Schedule pulumi refresh and alert on unexpected diffs. Same discipline as Terraform drift checks.
No policy-as-code
Use CrossGuard to block public S3 ACLs, unencrypted RDS instances, or missing tags before apply. Policy belongs in CI, not in a wiki page. Align with practices in our DevOps automation guide where appropriate.
On a production Laravel application, I've debugged more outages from misconfigured security groups than from application bugs. Pulumi makes SG rules readable in TypeScript, but someone still has to review the port list.
Key Takeaways
- Pulumi lets you write infrastructure in TypeScript, Python, Go, and other real languages—with loops, functions, packages, and IDE support.
- Pick Pulumi when programmatic abstraction and shared libraries matter more than HCL readability for ops reviewers.
- Run
pulumi previewon every pull request andpulumi uponly from CI after merge, with encrypted secrets and policy gates. - Split stacks by lifecycle and use component resources to avoid copy-paste across client environments.
- Small teams on fixed EC2 with Deployer may not need IaC yet—adopt Pulumi when environment count and complexity justify it.
- Pair Pulumi with existing support and maintenance workflows so infrastructure and application ownership stay documented.
People Also Ask
Is Pulumi free to use?
Pulumi's CLI and SDKs are open source. Individual developers can use Pulumi Cloud's free tier for state and collaboration. Teams needing SSO, audit logs, or advanced policy often pay for Pulumi Cloud or self-host state on S3 with the open-source backend options documented on pulumi.com.
Can Pulumi manage existing cloud resources?
Yes. Use pulumi import to adopt resources created manually or by another tool. Import maps a cloud resource ID to a Pulumi resource name in state. You then write matching code so the next preview shows no drift. Migration from Terraform typically involves importing or rebuilding stacks module by module.
Does Pulumi work with Kubernetes?
Pulumi ships first-class Kubernetes providers. You can deploy Helm charts, raw YAML, or CRDs from TypeScript or Python. Many teams use Pulumi for cluster add-ons while GitOps tools handle in-cluster app manifests. The choice depends on who owns cluster lifecycle versus application deploy.
Which language is best for Pulumi?
Use the language your team already maintains in production. TypeScript fits Node-heavy shops and delivers strong typing. Python suits data and backend teams. Go works for platform engineering groups that already write Kubernetes operators. There is no universal best—consistency with your app stack wins.
Ship infrastructure your application team can actually read
Pulumi: IaC in Real Programming Languages closes the gap between how you build Laravel APIs and how you provision the servers that run them. Start with one non-production stack, wire preview into GitLab CI, and extract repeated patterns into components before you multiply environments. If you want help evaluating Pulumi against your current Deployer and EC2 setup—or designing a full pipeline for a multi-client platform—contact us for a practical architecture review. You can also browse the Adventure Third Pole Trek portfolio for Laravel plus DevOps delivery examples, read more on the blog, or learn about custom software development and hosting setup for Nepal-based projects. Visit kokil.com.np or about me for background on production systems shipped since 2010.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

