You can destroy your infrastructure with Pulumi, but the real trick is doing it without accidentally wiping out production resources you didn’t intend to.
Let’s see it in action. Imagine you have a Pulumi program that creates an AWS S3 bucket and a DynamoDB table.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Create an S3 bucket
const bucket = new aws.s3.Bucket("my-app-bucket", {
acl: "private",
});
// Create a DynamoDB table
const table = new aws.dynamodb.Table("my-app-table", {
attributes: [
{ name: "id", type: "S" },
],
hashKey: "id",
billingMode: "PAY_PER_REQUEST",
});
// Export bucket name and table name
export const bucketName = bucket.id;
export const tableName = table.name;
When you run pulumi up, Pulumi provisions these resources. Now, if you decide you no longer need them, you’d typically run pulumi destroy.
But wait, what if you have multiple stacks? Or what if this bucket is actually used by a different, older application that you don’t want to touch? Pulumi, by default, operates on the current stack. If you’ve run pulumi stack select dev and then pulumi destroy, it will only destroy resources managed by the dev stack. If your prod stack also happens to manage a bucket with the same name (though this is bad practice), pulumi destroy on dev won’t touch it.
The key to safely tearing down is understanding Pulumi’s state management and resource targeting. Pulumi tracks every resource it creates, associates it with a specific stack, and stores its current state. When you run pulumi destroy, it consults this state for the current stack and then instructs the cloud provider to delete those specific resources. It doesn’t go looking for resources by name in your cloud account; it looks for resources it knows about within its state file for the active stack.
So, to destroy the resources from our example, you’d navigate to the directory containing your Pulumi program and run:
pulumi stack select dev # Ensure you are on the correct stack
pulumi destroy
Pulumi will then present you with a preview of what will be destroyed:
Previewing destroy (dev)
Type Name Plan
- pulumi:pulumi:Stack my-project-dev (approx. 1 minute)
- aws:s3:Bucket my-app-bucket delete
- aws:dynamodb:Table my-app-table delete
Resources: 3 destroyed
Do you truly want to destroy these resources? all yes
If you confirm, Pulumi will orchestrate the deletion. The aws:s3:Bucket and aws:dynamodb:Table resources are identified by Pulumi’s internal IDs, not just their names. This is crucial because it prevents accidental deletion of resources with the same name that Pulumi doesn’t manage.
The pulumi:pulumi:Stack resource is a Pulumi-specific resource that represents the stack itself. Destroying this cleans up Pulumi’s metadata for the stack.
What if you want to destroy only specific resources within a stack, not the whole stack? You can use the --target flag. For instance, to only destroy the S3 bucket:
pulumi destroy --target aws:s3/bucket:Bucket:my-app-bucket
This tells Pulumi to only consider the S3 bucket for deletion. It will still check for dependencies, but it won’t touch the DynamoDB table.
The most surprising thing is how granular Pulumi’s destruction can be, even across different cloud providers and resource types, all managed through a single destroy command or targeted deletions. It’s not just about deleting things named X; it’s about deleting the Pulumi-managed instance of X.
After successfully destroying all resources in a stack, the next logical step is often to remove the stack’s metadata entirely, which you can do with pulumi stack rm dev.