Skip to content

Architecture

Pulumi is an infrastructure-as-code platform that allows users to define cloud resources using general-purpose programming languages (TypeScript, Python, Go, .NET, Java) instead of a domain-specific language. The engine compiles user programs into resource declarations, then reconciles desired state against real infrastructure via provider plugins.


Component Overview

graph TB
    subgraph USER["User Program"]
        TS["TypeScript / Node.js"]
        PY["Python"]
        GO["Go"]
        CS[".NET / C#"]
        JA["Java"]
    end

    subgraph LH["Language Host Plugins"]
        LHN["Node.js Host"]
        LHP["Python Host"]
        LHG["Go Host"]
        LHD[".NET Host"]
    end

    subgraph ENGINE["Pulumi Engine"]
        RC["Resource Coordinator"]
        RG["Resource Graph<br/>(DAG)"]
        DISP["Dispatcher"]
        SNAP["Snapshot / State"]
    end

    subgraph PROV["Provider Plugins"]
        PAWS["AWS Provider"]
        PAZURE["Azure Provider"]
        PGCP["GCP Provider"]
        PK8S["Kubernetes Provider"]
        PBRIDGE["Terraform Bridge<br/>Providers"]
    end

    subgraph BACKEND["State Backend"]
        PCLOUD["Pulumi Cloud<br/>(SaaS)"]
        SELF["Self-managed<br/>(S3, Azure Blob, local)"]
        ESC["Pulumi ESC<br/>(Secrets & Config)"]
    end

    TS --> LHN
    PY --> LHP
    GO --> LHG
    CS --> LHD
    JA --> LHN

    LHN -->|gRPC| ENGINE
    LHP -->|gRPC| ENGINE
    LHG -->|gRPC| ENGINE
    LHD -->|gRPC| ENGINE

    RC --> RG
    RG --> DISP
    DISP -->|gRPC| PAWS
    DISP -->|gRPC| PAZURE
    DISP -->|gRPC| PGCP
    DISP -->|gRPC| PK8S
    DISP -->|gRPC| PBRIDGE

    RC --> SNAP
    SNAP --> PCLOUD
    SNAP --> SELF
    ESC --> RC

Engine

The Pulumi engine (pulumi-language-go, pulumi/pkg/engine) is the central orchestrator written in Go. It:

  1. Receives resource registration requests from language hosts via gRPC.
  2. Builds a dependency graph of all resources in the program.
  3. Dispatches CRUD operations to the appropriate provider plugin.
  4. Maintains a snapshot (state) of all deployed resources.
  5. Detects drift by comparing the snapshot with real infrastructure during refreshes.

The engine runs as a single process that communicates with language hosts and provider plugins as separate subprocesses.


Language Host Plugins

Unlike Terraform's HCL-only approach, Pulumi supports multiple programming languages through language host plugins. Each host is responsible for:

  • Executing the user's program.
  • Intercepting resource constructor calls (new aws.s3.Bucket(...), aws.s3.Bucket(...)).
  • Translating those calls into gRPC RegisterResource and RegisterResourceOutputs messages sent to the engine.
Language Host SDK Package Runtime
Node.js @pulumi/pulumi Node.js 18+
Python pulumi (pip) CPython 3.8+
Go github.com/pulumi/pulumi/sdk/v3/go/pulumi Go 1.21+
.NET Pulumi (NuGet) .NET 6+
Java com.pulumi (Maven) JDK 11+

The language host protocol is defined as a gRPC service (pulumirpc.LanguageRuntime) with methods:

  • GetRequiredPlugins -- declares which providers the program needs.
  • Run -- executes the program, streaming RegisterResource calls back to the engine.

Resource Model

Resource Types

Pulumi defines two primary resource abstractions:

Type Purpose Example
CustomResource Maps to a single cloud resource aws.s3.Bucket, azure.compute.VirtualMachine
ComponentResource Logical grouping of child resources awsx.ec2.Vpc, custom abstractions

Resource URN

Every resource is identified by a URN (Uniform Resource Name):

urn:pulumi:<stack>::<project>::<type>::<name>

Example:

urn:pulumi:production::my-app::aws:s3/bucket:Bucket::my-bucket

The URN encodes the stack, project, resource type, and logical name. It serves as the stable identifier across updates.

Inputs and Outputs

  • Inputs: Values passed to a resource constructor (props argument). Can be plain values, Promises, or Output<T>.
  • Outputs: Represent values that are not known until after deployment. Output<T> tracks dependencies automatically and propagates secrets.

Output<T> is Pulumi's core mechanism for chaining resource dependencies:

const bucket = new aws.s3.Bucket("my-bucket");
const obj = new aws.s3.BucketObject("my-object", {
    bucket: bucket.bucket,  // Output<string> -- dependency tracked
});

Provider Plugins

Providers are separate processes that communicate with the engine over gRPC (pulumirpc.ResourceProvider service).

Provider Lifecycle RPCs

sequenceDiagram
    participant Engine as Pulumi Engine
    participant Provider as Provider Plugin (e.g., aws)

    Engine->>Provider: GetSchema
    Provider-->>Engine: JSON Schema
    Engine->>Provider: Configure (credentials, region)
    Provider-->>Engine: ConfigureResponse

    loop For each resource
        Engine->>Provider: Check (validate inputs)
        Provider-->>Engine: CheckResponse
        Engine->>Provider: Diff (detect changes)
        Provider-->>Engine: DiffResponse
        alt Create
            Engine->>Provider: Create
            Provider-->>Engine: CreateResponse (id + outputs)
        else Update
            Engine->>Provider: Update
            Provider-->>Engine: UpdateResponse
        else Delete
            Engine->>Provider: Delete
            Provider-->>Engine: empty
        end
    end

Terraform Bridge

The Terraform Bridge (pulumi-terraform-bridge) wraps existing Terraform providers as Pulumi providers. This gives Pulumi access to 4,800+ providers without re-implementing them. The bridge translates between Pulumi's gRPC protocol and Terraform's provider protocol.

Key bridge components: - Schema translation: Converts Terraform provider schemas to Pulumi schemas. - Resource mapping: Maps Terraform resource types to Pulumi resource types. - State conversion: Translates between Terraform state format and Pulumi state format.


Resource Lifecycle Diagram

flowchart TD
    A[User runs pulumi up] --> B[Language host executes program]
    B --> C[Program registers resources via SDK]
    C --> D[Engine builds resource DAG]
    D --> E{Resource exists in state?}
    E -->|No| F[Create: provider.Create]
    E -->|Yes| G[Diff: provider.Diff]
    G --> H{Changes detected?}
    H -->|No change| I[No-op, keep existing]
    H -->|In-place update| J[Update: provider.Update]
    H -->|Requires replacement| K[Create new + Delete old]
    F --> L[Update state snapshot]
    J --> L
    K --> L
    I --> L
    L --> M{More resources?}
    M -->|Yes| E
    M -->|No| N[Persist final snapshot to backend]

State Management

State Backend Options

Backend Type State Locking Secrets Encryption
Pulumi Cloud Managed SaaS Yes Yes (per-secret, server-side)
Self-managed (S3) AWS S3 bucket Yes (DynamoDB) Local passphrase or cloud KMS
Self-managed (Azure Blob) Azure storage Yes (blob lease) Local passphrase
Self-managed (local) Local filesystem No Local passphrase

State Format

  • Stored as a JSON snapshot containing every resource's URN, type, inputs, outputs, and provider reference.
  • Secrets within state are individually encrypted (not just the whole file), allowing selective decryption.
  • Each update creates a new checkpoint (versioned history in Pulumi Cloud).

Stack References

Stack references allow one stack to read outputs from another:

const otherStack = new pulumi.StackReference("org/other-project/prod");
const vpcId = otherStack.getOutput("vpcId");

Automation API

The Automation API enables embedding Pulumi directly within application code, creating custom deployment workflows without shelling out to the CLI:

import * as auto from "@pulumi/automation";

const stack = await auto.LocalWorkspace.createOrSelectStack({
    stackName: "dev",
    projectName: "my-app",
    program: myPulumiProgram,
});

const upResult = await stack.up();
console.log(`Update succeeded: ${upResult.summary.result}`);

Key use cases: - Custom CLIs and developer portals. - CI/CD pipeline integration with fine-grained control. - Multi-stage deployment orchestrators. - SaaS provisioning systems.


Comparison with Terraform / OpenTofu

Aspect Pulumi Terraform / OpenTofu
Language General-purpose (TS, Python, Go, .NET, Java) HCL (declarative DSL)
Provider ecosystem Native + 4,800+ via Terraform Bridge Native providers only
State secrets Per-secret encryption Whole-file (OpenTofu) or none (Terraform OSS)
Testing Standard unit test frameworks Limited (plan assertions, Sentinel)
Automation API Built-in Not available (CLI-only)
Component reuse Standard language packages/modules Terraform modules (HCL)

References


How It Works

Language SDK runtime, provider plugin architecture, and deployment engine internals.

Deployment Engine

sequenceDiagram
    participant User as User Code (TypeScript/Python/Go)
    participant SDK as Pulumi Language SDK
    participant Engine as Pulumi Engine
    participant Provider as Provider Plugin (gRPC)
    participant Cloud as Cloud API

    User->>SDK: Declare resources (new aws.s3.Bucket(...))
    SDK->>Engine: Register resource (gRPC)
    Engine->>Engine: Build dependency graph
    Engine->>Provider: Create/Update/Delete
    Provider->>Cloud: API calls
    Provider-->>Engine: Resource outputs
    Engine-->>SDK: Resolve outputs (Promises/Futures)
    SDK-->>User: Export outputs

Language Runtime Model

flowchart TB
    subgraph User["User Process"]
        Code["TypeScript / Python / Go\nInfra Code"]
        SDK_P["Pulumi SDK\n(resource registration)"]
    end

    subgraph Engine_P["Pulumi Engine Process"]
        Deployment["Deployment Engine\n(DAG, diffing, ordering)"]
        StateM["State Manager"]
    end

    subgraph Providers_P["Provider Processes"]
        AWS_P["AWS Provider"]
        K8s_PP["K8s Provider"]
        TF_Bridge["Terraform Bridge\n(use TF providers)"]
    end

    Code --> SDK_P
    SDK_P <-->|"gRPC"| Deployment
    Deployment <-->|"gRPC"| Providers_P
    Deployment --> StateM

    style User fill:#8b5cf6,color:#fff
    style Engine_P fill:#1565c0,color:#fff

Terraform Bridge

Pulumi can use any Terraform provider via the Terraform Bridge -- a compatibility layer that translates Terraform provider schemas into Pulumi resource types. This gives Pulumi access to the same 4,800+ providers.

Bridge Internals

  1. Schema translation: The bridge reads the Terraform provider's JSON schema and generates Pulumi SDK types (TypeScript, Python, Go, and others)
  2. Resource lifecycle mapping: Terraform's Create/Read/Update/Delete CRUD functions map directly to Pulumi's provider gRPC interface
  3. State mapping: Terraform's flat state attributes are converted to Pulumi's structured resource outputs

Dependency Resolution

Pulumi automatically detects resource dependencies from program flow:

const bucket = new aws.s3.Bucket("my-bucket");
const obj = new aws.s3.BucketObject("my-object", {
    bucket: bucket.bucket,  // implicit dependency: obj depends on bucket
    source: new pulumi.asset.FileAsset("./dist/app.zip"),
});

The SDK captures all Output<T> references during resource registration. The engine builds a DAG from these references and executes independent resources in parallel while respecting dependency order.

State and Locking

Pulumi stores state in a backend (Pulumi Cloud, self-managed, or local filesystem):

  • Checkpoint format: State is stored as a JSON blob containing all resource URNs, IDs, outputs, and dependency information
  • Locking: The backend provides optimistic concurrency control via version numbers. If two concurrent updates attempt to modify the same stack, the second will fail with a conflict error
  • Secrets: Sensitive outputs are encrypted at rest using a per-stack encryption key

Sources


Benchmarks

Scope

Performance characteristics, scaling limits, and resource consumption for Pulumi.

Deployment Performance

Stack Size Preview Time Update Time Notes
50 resources 5-10s 1-3m Quick iteration
200 resources 15-30s 3-10m Typical app stack
1,000 resources 1-3m 10-30m Large infrastructure

Language Runtime Overhead

Language Cold Start Warm Start Memory
Go 1-2s < 1s 50-100MB
TypeScript 3-5s 1-2s 100-200MB
Python 2-4s 1-2s 80-150MB
C# 3-6s 1-3s 100-200MB

Scaling Limits

Dimension Limit Notes
Resources per stack 10,000+ Performance degrades beyond 5,000
Stacks per project Unlimited Use stack references for composition
Concurrent operations 1 per stack Stack-level locking

Sourcing Status

Unsourced Performance Data

Do not plan capacity from these numbers. We estimated them from vendor documentation, community benchmarks, and engineering judgment. They do not represent controlled benchmarks with documented test conditions. Specific hardware configurations, software versions, and test methodologies were not recorded.

Use these figures as rough guidance only. For production capacity planning, run your own benchmarks against your specific workload and infrastructure.

Sources


Security

Authentication Model

Pulumi supports multiple authentication methods for accessing Pulumi Cloud and managing infrastructure:

Method Use Case
Personal Access Token Individual developer CLI authentication
Team/Organization Tokens CI/CD pipelines and automation
SSO (SAML/OIDC) Enterprise identity federation (Okta, Azure AD, and others)
SCIM Automated user provisioning and deprovisioning
GitHub/GitLab SSO Developer-friendly login via existing accounts
graph TB
    subgraph Users
        Dev["Developer\n(CLI / IDE)"]
        CI["CI/CD Pipeline\n(GitHub Actions)"]
    end
    subgraph PulumiCloud["Pulumi Cloud"]
        AuthN["Identity Provider\n(SAML/OIDC/SCIM)"]
        RBAC["RBAC Engine\n(Org/Team/Stack)"]
        StateStore["Encrypted State\n(HSM-backed)"]
        ESC["ESC\n(Secrets Manager)"]
    end
    subgraph Providers["Cloud Providers"]
        AWS["AWS"]
        Azure["Azure"]
        GCP["GCP"]
    end
    Dev --> AuthN
    CI --> AuthN
    AuthN --> RBAC
    RBAC --> StateStore
    RBAC --> ESC
    RBAC --> Providers

Secrets Management

Built-in Secrets Encryption

Pulumi encrypts individual secret values within the stack state, providing fine-grained protection:

  • Pulumi Cloud secrets provider: Encryption/decryption happens server-side. The client never gets the key. HSM-backed encryption at rest.
  • Passphrase-based: Local backend uses a passphrase to derive an encryption key (AES-256-GCM).
  • Cloud KMS backends: AWS KMS, Azure Key Vault, GCP KMS for customer-managed keys.
# Set a secret config value
pulumi config set --secret db-password "s3cureP@ss"

# Use in code
config.requireSecret("db-password")

Secrets are tracked transitively — if a secret value is used in a computation, the output is also treated as a secret and masked in CLI output and the Pulumi Cloud console.

Pulumi ESC (Environments, Secrets, and Configuration)

ESC provides centralized secrets management with:

  • Hierarchical environments: Inherit and override secrets across org, team, and project scopes
  • Dynamic credentials: Generate short-lived AWS/Azure/GCP credentials on demand via OIDC
  • External secret stores: Pull from HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, 1Password
  • Access methods: CLI, API, Kubernetes operator, UI, in-code SDK (TypeScript, Python, Go)

Authorization and RBAC

Pulumi Cloud RBAC

Role Scope Permissions
Org Admin Organization Manage members, teams, billing, all stacks
Team Admin Team Manage team membership, team stacks
Contributor Team/Stack Read/write stacks, run updates
Reader Stack Read-only access to stack state and outputs
Deploy Admin Stack Approve/trigger deployments (Pulumi Deployments)

Stack-Level Access Control

Stacks support granular permissions independent of team membership:

  • Protection policies: Lock stacks against destructive changes
  • Revision history: Full audit trail of all state changes
  • Concurrent update prevention: State locking prevents conflicting updates

CrossGuard Policy as Code

Pulumi CrossGuard enforces organizational policies during previews and updates:

import * as pulumi from "@pulumi/pulumi";

new pulumi.Policy("s3-encryption", {
    enforcementLevel: "mandatory",
    validateResource: (args, reportViolation) => {
        if (args.type === "aws:s3/bucketV2:BucketV2") {
            if (!args.props.serverSideEncryptionConfiguration) {
                reportViolation("S3 buckets must have encryption enabled");
            }
        }
    },
});

Policy packs can be set to advisory (warn), mandatory (block), or disabled per stack.

Provider Credential Security

Dynamic Provider Credentials

Instead of static credentials, use OIDC-based dynamic credentials:

Provider Method
AWS sts:AssumeRoleWithWebIdentity via Pulumi Cloud OIDC
Azure Workload Identity Federation
GCP Workload Identity Federation

Credential Best Practices

  • Never store cloud provider credentials in Pulumi config — use OIDC or ESC
  • Use pulumi config set --secret for any sensitive configuration
  • Rotate personal access tokens regularly. Prefer organization tokens for CI
  • Enable SSO for all team members to enforce corporate identity policies

Stack References Security

Stack references allow one stack to read outputs from another:

const infra = new pulumi.StackReference("org/project/infra-stack");
const vpcId = infra.getOutput("vpcId");

Security Considerations

  • Restrict read access first. Anyone with read access to the referenced stack sees all stack outputs
  • Use RBAC to restrict which teams can read sensitive stack outputs
  • Secret outputs remain encrypted when accessed via stack references
  • Consider separate organizations for complete isolation between environments

State File Security

  • State stored in HSM-backed encrypted storage
  • All data encrypted in transit (TLS 1.2+) and at rest (AES-256)
  • State files are never written to disk on the client machine during operations
  • Full audit log of all state changes accessible in the Pulumi Cloud console

Self-Managed Backends

When using self-managed backends (S3, Azure Blob, GCS):

  • Enable server-side encryption (SSE-S3, SSE-KMS, or customer-managed keys)
  • Enable versioning to protect against accidental deletion
  • Restrict bucket access using IAM policies
  • Enable access logging for audit trail
  • Use a secrets provider (not passphrase) for production stacks

Hardening Checklist

  • Enable SSO (SAML or OIDC) for all organization members
  • Use dynamic provider credentials (OIDC) instead of static access keys
  • Store all sensitive values with pulumi config set --secret
  • Enable CrossGuard policy packs with mandatory enforcement
  • Use Pulumi Cloud as the state backend (HSM-backed encryption)
  • Configure team-based RBAC with least-privilege access
  • Enable ESC for centralized secrets management
  • Review stack revision history periodically for unauthorized changes
  • Rotate organization tokens and personal access tokens quarterly
  • Enable audit logging for compliance tracking

Known Pitfalls

Pitfall Risk Mitigation
Plaintext secrets in config Credential exposure in version control Always use --secret flag
Static AWS keys in CI Long-lived credential compromise Use OIDC dynamic credentials
Broad org admin membership Overprivileged users Use least-privilege RBAC roles
Unprotected stack references Cross-stack data leakage Restrict stack read access via RBAC
Passphrase state encryption Weak passphrase equals weak encryption Use cloud KMS backends for production