Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Pulumi: IaC in Real Programming Languages

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.

Pulumi: IaC in Real Programming LanguagesYour CodeTS / Python / GoPulumi EnginePreview + ApplyState BackendS3 / Pulumi CloudProvider SDKsAWS / Azure / GCPPolicy + SecretsCrossGuard / KMSLive Cloud ResourcesEC2, RDS, S3, Lambda, EKS — same APIs Terraform targets
How Pulumi turns real programming language source into cloud resources with tracked state

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.

CriterionPulumi (real languages)Terraform (HCL)CloudFormation (YAML/JSON)
LanguageTypeScript, Python, Go, C#, Java, YAMLHCL + limited expressionsYAML or JSON templates
AbstractionFunctions, classes, npm/PyPI packagesModules; logic via HCL functionsNested stacks, macros
TestingUnit tests with mocks (Jest, pytest)Terraform test (HCL), terratest (Go)TaskCat, manual drift checks
IDE supportFull autocomplete, jump-to-def, refactorGood with HCL extensionBasic schema validation
StatePulumi Service, S3, localRemote backend (S3, Terraform Cloud)Managed by AWS
Multi-cloudStrongStrongAWS only
Learning curveLow if team knows the languageMedium—new DSLMedium—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.

  1. Run pulumi login — use Pulumi Cloud or configure an S3 backend.
  2. Create a project: mkdir pulumi-demo && cd pulumi-demo && pulumi new aws-typescript
  3. Select defaults, then run pulumi up to preview and apply.
  4. 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.

Pulumi Project LayoutPulumi.yamlProject metadataindex.ts / __main__.pyResource declarationsStack: productionPulumi.prod.yaml configStack: stagingPulumi.staging.yaml configComponentsReusable modulesVpcComponent classOne repo — many stacks — shared component librarySame pattern as monorepo apps your Laravel team already runs
Pulumi project structure: one codebase, multiple stacks, reusable component resources

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.

Pulumi CI/CD PipelineGit PushUnit Testspulumi previewPR ReviewPolicy CheckCrossGuard rulespulumi upMerge to mainProduction stack updated — app deploy via Deployer followsSeparate infra and app pipelines reduce blast radius
Recommended Pulumi CI/CD flow: preview on pull requests, policy checks, apply only after merge

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 .tf artifacts.
  • 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.

Pulumi vs Terraform DecisionNeed IaC at all?NoManual + DeployerSmall static stacksYesTeam language?TS / Python / GoPulumiReal languagesTerraformOps prefers HCLMatch tool to team skills — not hype
Decision tree: when Pulumi IaC in real programming languages beats Terraform or manual provisioning

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 preview on every pull request and pulumi up only 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

Pulumi lets you define cloud resources in TypeScript, Python, Go, C#, Java, or YAML. The CLI runs your program, calls cloud APIs, and stores tracked state.

Terraform reads static HCL files; Pulumi runs a full programming language program with loops, functions, classes, and package imports. Both reach major clouds and track state, but Pulumi offers full IDE autocomplete, refactor support, and unit tests with Jest or pytest. Terraform wins where ops teams prefer HCL diffs in pull requests and a mature public module registry. Pulumi fits teams that already write TypeScript or Python daily and need shared libraries across dozens of similar stacks. On small Deployer-managed EC2 setups, either tool may be overkill until environment count grows.

Yes. The CLI and SDKs are open source. Individual developers get a free Pulumi Cloud tier for state. Teams needing SSO or audit logs typically pay or self-host state on S3.

Install the CLI from pulumi.com/docs/install. For TypeScript you need Node.js 26 LTS; for Python, Python 3.x. Run pulumi login against Pulumi Cloud or an S3 backend, then pulumi new aws-typescript in a new directory. Set stack config, run pulumi up to preview and apply, and pulumi destroy when finished. A minimal TypeScript program imports @pulumi/pulumi and @pulumi/aws, reads config with pulumi.Config(), and declares resources like an S3 bucket with tags. TypeScript compile-time checks catch typos before CI runs.

Use whichever language your team already maintains in production. There is no universal best choice.

CloudFormation uses YAML or JSON templates managed entirely by AWS, so it is AWS-only. Pulumi supports strong multi-cloud coverage like Terraform. CloudFormation offers nested stacks and macros but limited IDE support beyond basic schema validation. Pulumi gives full autocomplete, jump-to-definition, and refactor tools in TypeScript or Python. Testing CloudFormation often means TaskCat or manual drift checks; Pulumi supports unit tests with mocks via Jest or pytest beside your application test suite. For Laravel teams on AWS who want programmatic logic and shared typed libraries, Pulumi is usually more productive than verbose CloudFormation templates.

Yes. Run pulumi import to adopt resources created manually or by another tool. Import maps a cloud resource ID to a Pulumi resource name in your state file. You then write matching declarative code so the next pulumi preview shows no drift. Migration from Terraform typically proceeds stack by stack, either importing existing resources or rebuilding modules incrementally. This matters when you have hand-provisioned EC2 or S3 buckets and want IaC without tearing everything down first. After import, treat the code as the source of truth and run preview on every pull request.

Pulumi ships first-class Kubernetes providers. You can deploy Helm charts, raw YAML, or custom resource definitions from TypeScript or Python programs. Many teams use Pulumi for cluster add-ons and baseline infrastructure while GitOps tools handle in-cluster application manifests. The split depends on who owns cluster lifecycle versus who owns application deploy pipelines like Deployer for Laravel. Pulumi fits when you want the same language and testing workflow for VPCs, node pools, and cluster-level resources. Pair it with your existing CI preview gates so infrastructure changes never apply directly from a laptop.

Production Pulumi should never run from a laptop. Mirror Terraform discipline: pulumi preview on merge requests, pulumi up only after merge to main. Use the pulumi/pulumi-nodejs image, run npm ci, select the target stack with pulumi stack select, then pulumi preview --non-interactive or pulumi up --yes --non-interactive. Store PULUMI_ACCESS_TOKEN and cloud credentials in GitLab CI variables, never in the repo. Use encrypted config for database passwords. Application deploy stays on Deployer; infrastructure deploy stays on Pulumi—a clear boundary I use on sister sites to avoid midnight surprises when PHP releases and VPC changes collide.

Choose Pulumi when your team writes TypeScript or Python daily, you need shared libraries across dozens of stacks, or you want unit tests beside infrastructure code—for example spinning up isolated preview environments per Git branch. Stay on Terraform when ops staff prefer HCL diffs, you rely on a mature module registry, or auditors expect .tf artifacts. Skip both when you run one or two EC2 instances with Deployer and manual provisioning, common on budget-sensitive Nepal client projects. Adopt Pulumi when environment count and programmatic abstraction justify the toolchain, not because the founding developer prefers JavaScript.

Side effects outside resource declarations—calling live APIs during every preview slows CI and causes flaky plans. Secrets stored in plain stack YAML instead of pulumi config set --secret. Monolithic stacks that increase apply time and blast radius; split network, data, and app stacks and link them with StackReference. Skipping drift detection when someone changes resources in the console; schedule pulumi refresh. No policy-as-code; use CrossGuard to block public S3 ACLs or unencrypted RDS before apply. On production Laravel apps I have debugged more outages from misconfigured security groups than application bugs—readable TypeScript does not replace careful port reviews.

You choose the backend. Pulumi Cloud offers a free tier for individuals and handles collaboration. Teams can self-host state on object storage like S3 or use local files for experiments only. Secrets can be encrypted with a passphrase or a cloud KMS key. Never rely on local state for production—the same rule applies to Terraform remote backends. For teams already scanning IaC in CI, state location does not change your security workflow; gate previews and applies through GitLab CI with locked credentials regardless of whether state lives in Pulumi Cloud or your own bucket.

Component resources let you wrap repeated patterns into reusable classes. Instead of copying S3, VPC, or security group blocks across stacks, you define a typed module once and import it like an npm or PyPI package. The article shows a LaravelSiteComponent that provisions RDS, ElastiCache Redis 8.10, an ALB, and an EC2 autoscaling group with PHP 8.5 and Nginx user-data. Application teams call new LaravelSite with per-site overrides—domain, database engine—mirroring how I share Deployer recipes across legal-tech sister sites, but with compile-time checks and versioned packages instead of copy-pasted shell scripts.

Never put passwords in Pulumi.prod.yaml as plain text. Use pulumi config set --secret for values like dbPassword so Pulumi encrypts them in stack config. Pair stack secrets with AWS Secrets Manager or Parameter Store for runtime secrets consumed by Laravel apps after deploy. Store PULUMI_ACCESS_TOKEN and cloud API credentials in GitLab CI variables, rotated the same way you rotate Deployer SSH keys. Pulumi supports encryption via passphrase or cloud KMS keys depending on your backend choice. Treat secret handling with the same discipline as application .env files—one leaked config file in git can compromise an entire client stack.

One Pulumi stack maps to one deployable unit with its own state file—production, staging, or a client-specific environment each get separate stacks fed by stack-specific config. Avoid one monolithic stack for everything; split by lifecycle into network, data, and application stacks to shrink blast radius and shorten apply times. Use StackReference to read outputs across stacks, similar to Terraform remote state data sources. Start with one non-production stack, wire preview into GitLab CI, and extract repeated patterns into component resources before multiplying environments. That path avoids the trap of a single long apply that blocks every infrastructure change.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: