Project Organization
Why Organization Matters
The Problem: As infrastructure grows, monolithic Pulumi programs become unmaintainable. Teams step on each other's toes and deployments take forever.
The Solution: Follow proven patterns for splitting projects, organizing stacks, and creating reusable components that scale with your team.
Real Impact: Well-organized Pulumi projects reduce deployment times by 70% and enable teams to work independently on shared infrastructure.
Real-World Analogy
Think of Pulumi project organization like organizing a large company:
- Projects = Departments (networking, compute, databases) with clear responsibilities
- Stacks = Environments (dev, staging, prod) running the same department blueprints
- Component Resources = Standard operating procedures reused across departments
- Stack References = Inter-department communication channels
Project Structure Patterns
Micro-Stacks
Split infrastructure into small, focused stacks (networking, compute, database). Reduces blast radius and enables parallel development.
Mono-Repo
Keep all Pulumi projects in one repository. Simplifies cross-project changes, shared libraries, and atomic commits.
Component Libraries
Extract common patterns into shared npm packages. Standardize VPC configs, database setups, and monitoring across teams.
Config-Driven
Use Pulumi config and stack-specific settings to parameterize deployments. Same code, different environments.
Recommended Directory Layout
infra/
shared/
components/ # Reusable component resources
vpc.ts
database.ts
monitoring.ts
package.json # Shared as npm package
networking/
index.ts # VPC, subnets, DNS
Pulumi.yaml
Pulumi.dev.yaml
Pulumi.prod.yaml
compute/
index.ts # EKS, EC2, Lambda
Pulumi.yaml
database/
index.ts # RDS, DynamoDB
Pulumi.yaml
policy-pack/
index.ts # CrossGuard policies
.github/workflows/
preview.yml
deploy.yml
State Management
State Backend Options
# Use Pulumi Cloud (recommended for teams)
pulumi login
# Use S3 backend for self-managed state
pulumi login s3://my-state-bucket
# Use Azure Blob Storage
pulumi login azblob://my-state-container
# Refresh state to detect drift
pulumi refresh
# Export state for backup
pulumi stack export --file state-backup.json
# Import state from backup
pulumi stack import --file state-backup.json
Security Best Practices
import * as pulumi from "@pulumi/pulumi";
const config = new pulumi.Config();
// ALWAYS use secrets for sensitive values
const dbPassword = config.requireSecret("dbPassword");
const apiKey = config.requireSecret("apiKey");
// Set secrets via CLI (encrypted in state)
// pulumi config set --secret dbPassword "super-secret"
// Use OIDC for CI/CD instead of long-lived credentials
// Configure in Pulumi Cloud: ESC environments with OIDC
// Apply least-privilege IAM policies
const appRole = new aws.iam.Role("app-role", {
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Principal: { Service: "lambda.amazonaws.com" },
Action: "sts:AssumeRole",
}],
}),
// Use specific actions, never use "*"
});
Performance Optimization
| Technique | Impact | How To |
|---|---|---|
| Micro-Stacks | Faster deploys, smaller blast radius | Split by service or team ownership |
| Parallel Operations | Faster resource creation | Pulumi auto-parallelizes independent resources |
| Targeted Updates | Deploy only changed resources | pulumi up --target urn:... |
| Refresh Sparingly | Avoid slow API calls | Only refresh when drift is suspected |
| Provider Versions | Predictable builds | Pin exact versions in package.json |
Team Collaboration
Team Workflow Recommendations
- Code Review: Treat infrastructure PRs like application PRs. Review the preview output alongside code changes.
- Stack Ownership: Assign clear stack ownership. Use Pulumi teams and RBAC to control who can deploy where.
- Naming Conventions: Use consistent naming:
org/project/stackformat (e.g.,acme/networking/prod). - Documentation: Document stack outputs and their consumers. Use stack tags for metadata.
- Drift Detection: Schedule periodic
pulumi refreshto catch manual changes made outside Pulumi. - Rollback Plan: Always have a rollback strategy. Use
pulumi stack exportbefore risky changes.
Quick Reference
Tagging Strategy
// Apply consistent tags using transformations
pulumi.runtime.registerStackTransformation((args) => {
if (args.props.tags !== undefined) {
args.props.tags = {
...args.props.tags,
"pulumi:project": pulumi.getProject(),
"pulumi:stack": pulumi.getStack(),
"ManagedBy": "Pulumi",
"Environment": pulumi.getStack(),
};
}
return { props: args.props, opts: args.opts };
});