Understanding Projects
Why Projects & Stacks Matter
The Problem: As infrastructure grows, you need a way to organize code and deploy the same infrastructure to multiple environments without duplication.
The Solution: Pulumi projects define what infrastructure looks like, while stacks let you deploy multiple instances (dev, staging, prod) with different configurations.
Real Impact: Teams can safely test infrastructure changes in dev before promoting to production, all from the same codebase.
Real-World Analogy
Think of projects and stacks like a cookie cutter and cookies:
- Project = The cookie cutter (the shape/template)
- Stack = Each individual cookie (an instance of that template)
- Configuration = The dough flavor (chocolate for dev, vanilla for prod)
- Pulumi.yaml = The recipe card that identifies the cookie cutter
- Stack Outputs = The label on each cookie box telling you what's inside
What is a Pulumi Project?
Single Codebase
A project is a directory containing your Pulumi program, identified by a Pulumi.yaml file at the root.
Language Runtime
Each project specifies a runtime (nodejs, python, go, dotnet, java) that determines how code is executed.
Multiple Stacks
A single project can have many stacks, each representing an isolated deployment environment.
Version Controlled
Projects live in your Git repository alongside application code, enabling GitOps workflows.
Project Structure
my-infrastructure/
├── Pulumi.yaml # Project definition
├── Pulumi.dev.yaml # Dev stack config
├── Pulumi.staging.yaml # Staging stack config
├── Pulumi.prod.yaml # Production stack config
├── index.ts # Main program entry point
├── network.ts # Network resources
├── database.ts # Database resources
├── package.json # Node.js dependencies
└── tsconfig.json # TypeScript config
The Pulumi.yaml File
name: my-infrastructure
runtime:
name: nodejs
options:
typescript: true
description: Core infrastructure for our application
config:
pulumi:tags:
value:
pulumi:template: aws-typescript
Working with Stacks
Creating and Managing Stacks
# Create a new stack
pulumi stack init dev
pulumi stack init staging
pulumi stack init prod
# List all stacks
pulumi stack ls
# Switch between stacks
pulumi stack select dev
# View current stack
pulumi stack
# Remove a stack (must destroy resources first)
pulumi stack rm dev
Stack Configuration
config:
aws:region: us-east-1
my-infrastructure:instanceType: t3.micro
my-infrastructure:minInstances: "1"
my-infrastructure:dbInstanceClass: db.t3.small
Reading Configuration in Code
import * as pulumi from "@pulumi/pulumi";
const config = new pulumi.Config();
// Required config (throws if missing)
const instanceType = config.require("instanceType");
// Optional config with default
const minInstances = config.getNumber("minInstances") || 1;
// Stack name for resource naming
const stack = pulumi.getStack(); // "dev", "staging", "prod"
// Use stack name in resource names
const bucketName = `app-data-${stack}`;
Stack Outputs
# View all outputs
pulumi stack output
# Get a specific output
pulumi stack output bucketName
# Get output as JSON
pulumi stack output --json
# Use output in scripts
BUCKET=$(pulumi stack output bucketName)
echo "Deploying to $BUCKET"
Multi-Stack Patterns
Common Multi-Stack Strategies
- Environment-per-stack: dev, staging, prod stacks in one project (most common)
- Region-per-stack: us-east-1, eu-west-1 stacks for multi-region deployments
- Team-per-stack: team-a, team-b stacks sharing the same infrastructure pattern
- Feature-branch stacks: Create ephemeral stacks for PR review environments
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const stack = pulumi.getStack();
const config = new pulumi.Config();
// Environment-specific sizing
const isProd = stack === "prod";
const instanceType = isProd ? "t3.large" : "t3.micro";
const minSize = isProd ? 3 : 1;
// All resources automatically tagged with environment
const defaultTags = {
Environment: stack,
ManagedBy: "pulumi",
Project: pulumi.getProject(),
};
Common Pitfall
Problem: Accidentally deploying to the wrong stack (e.g., destroying prod instead of dev).
Solution: Always run pulumi stack before pulumi up or pulumi destroy to confirm which stack is active. Use --stack flag for explicit targeting.
Quick Reference
Stack Management Commands
| Command | Description | Example |
|---|---|---|
pulumi stack init |
Create a new stack | pulumi stack init prod |
pulumi stack select |
Switch active stack | pulumi stack select dev |
pulumi stack ls |
List all stacks | pulumi stack ls |
pulumi stack output |
View stack outputs | pulumi stack output vpcId |
pulumi stack rm |
Remove a stack | pulumi stack rm dev --yes |
pulumi stack tag |
Manage stack tags | pulumi stack tag set env dev |
pulumi stack export |
Export stack state | pulumi stack export --file state.json |