Architecture¶
See also
index | iac/pulumi/explanation | iac/pulumi/how-to-guides | | index#Questions
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:
- Receives resource registration requests from language hosts via gRPC.
- Builds a dependency graph of all resources in the program.
- Dispatches CRUD operations to the appropriate provider plugin.
- Maintains a snapshot (state) of all deployed resources.
- 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
RegisterResourceandRegisterResourceOutputsmessages 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, streamingRegisterResourcecalls 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):
Example:
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 (
propsargument). Can be plain values, Promises, orOutput<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¶
- Pulumi documentation
- Pulumi GitHub repository
- Provider implementation guide
- Automation API documentation
- Terraform Bridge
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¶
- Schema translation: The bridge reads the Terraform provider's JSON schema and generates Pulumi SDK types (TypeScript, Python, Go, and others)
- Resource lifecycle mapping: Terraform's
Create/Read/Update/DeleteCRUD functions map directly to Pulumi's provider gRPC interface - 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 --secretfor 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¶
Pulumi Cloud (Recommended)¶
- 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
mandatoryenforcement - 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 |