Pulumi’s YAML configuration feels like a Swiss Army knife, but most people only ever use the screwdriver.
Let’s see Pulumi YAML in action. Imagine we have a simple Pulumi program in TypeScript that provisions an AWS S3 bucket.
// index.ts
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("my-bucket", {
acl: "private",
});
export const bucketName = bucket.id;
Now, instead of running pulumi up directly, we can define our project and stack configuration in a Pulumi.yaml file. Here’s a basic example:
# Pulumi.yaml
name: my-aws-project
runtime:
name: nodejs
options:
typescript: true
description: A minimal Pulumi AWS S3 bucket example.
This Pulumi.yaml file tells Pulumi about our project’s name, the runtime it uses (Node.js with TypeScript), and a brief description.
To manage different environments or deployment targets (like dev, staging, prod), Pulumi uses "stacks." Each stack can have its own configuration values. We can create a stack using pulumi stack init dev and then configure it.
Let’s say we want to set the AWS region for our dev stack. We can do this in a Pulumi.dev.yaml file:
# Pulumi.dev.yaml
config:
aws:region: us-east-1
When we run pulumi up, Pulumi automatically loads the configuration from Pulumi.yaml and any stack-specific configuration files like Pulumi.dev.yaml. The aws:region setting is a built-in configuration key for the AWS provider.
Here’s how the full mental model breaks down:
-
The Project (
Pulumi.yaml): This is the blueprint for your entire infrastructure as code effort. It defines the project’s name, the programming language and runtime you’re using (e.g.,nodejs,python,go,dotnet), and any common options for that runtime. It’s the foundational file that Pulumi commands operate on. It doesn’t contain specific infrastructure details, but rather the how of running your Pulumi code. -
Stacks: Think of stacks as distinct instances or environments of your project. You might have a
devstack, astagingstack, and aprodstack, all derived from the samePulumi.yaml. Each stack has its own state file, which tracks the resources it manages, and its own configuration values. -
Stack Configuration (
Pulumi.<stack-name>.yaml): This is where the magic of environment-specific settings happens. For each stack, you can create a corresponding YAML file (e.g.,Pulumi.dev.yaml,Pulumi.prod.yaml). These files contain key-value pairs that override or provide default settings for your Pulumi program. These can be provider-specific settings (likeaws:region) or custom configuration values you define in your code. -
Configuration Values in Code: Your Pulumi program can access these configuration values. For example, in TypeScript, you’d use
pulumi.Config:// index.ts (updated) import * as pulumi from "@pulumi/pulumi"; import * as aws from "@pulumi/aws"; const config = new pulumi.Config(); const awsRegion = config.require("aws:region"); // Accessing the config value const bucket = new aws.s3.Bucket("my-bucket", { acl: "private", region: awsRegion, // Using the config value }); export const bucketName = bucket.id;When you run
pulumi upwith thedevstack, Pulumi readsPulumi.dev.yaml, findsaws:region: us-east-1, and passes that value to yourindex.tsprogram. If you had aPulumi.prod.yamlwithaws:region: us-west-2, runningpulumi up --stack prodwould use that region. -
pulumi config set: This command-line tool is a convenient way to manage your stack configuration without directly editing YAML files. For instance,pulumi config set aws:region us-east-1 --stack devachieves the same result as creatingPulumi.dev.yamlwith that content. The CLI will update the appropriate stack configuration file.
The Pulumi.yaml file is not just a simple metadata holder; it actively dictates how your Pulumi program is executed by specifying the runtime and its associated options. This means you can have different runtimes for different projects, or even use specific compiler flags or interpreter versions if your runtime supports it via the options field. For example, for Node.js, you can specify a Node.js version:
# Pulumi.yaml (with Node.js version option)
name: my-node-project
runtime:
name: nodejs
options:
typescript: true
nodeVersion: "18.17.0" # Explicitly set Node.js version
description: A project requiring a specific Node.js version.
This ensures that your Pulumi program runs with a consistent Node.js environment, regardless of what’s installed globally on your machine. Pulumi will use this information to set up the execution context for your program.
When you use pulumi up, the system merges configurations in a specific order: defaults, then Pulumi.yaml (if it contains config), then Pulumi.<stack-name>.yaml, and finally any configuration values set directly on the command line or via pulumi config set. This layered approach provides immense flexibility for managing complex deployments across various environments.
The pulumi config command, when used without set, will display the current stack’s configuration values, which is incredibly useful for debugging and understanding what settings are active for a given deployment. It also allows you to remove configuration values using pulumi config rm <key>.
Beyond provider-specific configurations like aws:region, you can define your own arbitrary configuration keys. For instance, if you wanted to parameterize the instance size of an EC2 instance, you could add my-app:instanceSize: t3.micro to your Pulumi.dev.yaml and then access it in your code:
// index.ts (with custom config)
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const instanceSize = config.get("my-app:instanceSize") || "t3.micro"; // Provide a default
const server = new aws.ec2.Instance("app-server", {
ami: "ami-0abcdef1234567890", // Example AMI ID
instanceType: instanceSize,
// ... other instance configuration
});
This mechanism of layered configuration files and the pulumi config command is the core of how Pulumi enables robust, repeatable, and environment-aware infrastructure deployments.
The next step after mastering project and stack configuration is understanding how to manage secrets within Pulumi.