Pulumi stack outputs are how you make values from one Pulumi stack available to other Pulumi stacks, even across different projects.
Let’s say you have a project that deploys a database, and another project that deploys an application that needs to connect to that database. You can’t hardcode the database connection string into the application project because the database might not exist yet, or it might be deployed by a different team, or you might want to deploy them independently. Stack outputs solve this.
Here’s a database project:
import pulumi
import pulumi_aws as aws
# Create an RDS instance
db = aws_rds.Instance("mydb",
instance_class="db.t3.micro",
allocated_storage=20,
engine="mysql",
engine_version="8.0",
username="admin",
password="your-secure-password", # In a real scenario, use Pulumi secrets or a secret manager
skip_final_snapshot=True)
# Export the connection string as a stack output
pulumi.export("db_connection_string", pulumi.Output.format("mysql://admin:your-secure-password@{0}:3306/mydb", db.address))
When you run pulumi up in this database project, Pulumi will create the database and then display the output:
Outputs:
db_connection_string: mysql://admin:your-secure-password@rds.amazonaws.com:3306/mydb
Now, let’s create an app project that needs this connection string. The key is referencing the output from the database stack.
In your app project’s __main__.py:
import pulumi
import pulumi_aws as aws
# Reference the stack output from the 'database' stack.
# The first argument is the stack name (e.g., "my-org/my-project/database")
# The second argument is the name of the output to retrieve ("db_connection_string")
db_stack_ref = pulumi.StackReference("my-org/my-project/database")
db_connection_string = db_stack_ref.require_output("db_connection_string")
# Use the connection string to configure the application
app_server = aws.ec2.Instance("myapp",
ami="ami-0c55b159cbfafe1f0", # Example AMI ID
instance_type="t2.micro",
# Imagine this is passed to user_data or an environment variable
user_data=pulumi.Output.concat("#!/bin/bash\necho 'Connecting to DB: ", db_connection_string, "' > /tmp/db_info.txt")
)
pulumi.export("app_public_ip", app_server.public_ip)
When you run pulumi up in the app project, Pulumi automatically fetches the specified output from the database stack and uses it as an input for the app project’s resources. The db_connection_string becomes a dynamic input that Pulumi tracks. If the database connection string changes, Pulumi knows the application stack needs to be updated.
The pulumi_aws.rds.Instance resource (and most other resources) have attributes like address or endpoint that Pulumi exposes as outputs. You can see available outputs in the Pulumi CLI by running pulumi stack output --show-all in the source stack’s directory, or by inspecting the Pulumi Service UI. The pulumi.export() function is the explicit mechanism to make these internal resource attributes visible as stack outputs. pulumi.StackReference is the way to consume them in a different stack.
The require_output method is crucial here. If the specified output (db_connection_string in this case) doesn’t exist on the referenced stack, require_output will cause the pulumi up command to fail with an error, preventing you from deploying an incomplete infrastructure. This enforces dependencies and ensures that required values are present before they are used.
You can also reference outputs from stacks in different projects entirely. The StackReference constructor takes a fully qualified stack name: organization-name/project-name/stack-name. If you are referencing a stack within the same project, you can omit the organization and project names, e.g., pulumi.StackReference("database").
This mechanism forms the backbone of reusable infrastructure components in Pulumi. You can create a "base" stack that provisions core services like VPCs, databases, or Kubernetes clusters, and then have multiple application stacks reference the outputs of these base stacks to connect to and utilize those services. This promotes a modular and declarative approach to infrastructure management, where dependencies are clearly defined and managed by the Pulumi engine.
What most people don’t realize is that pulumi.Output objects are inherently lazy and asynchronous. When you pass an Output to a resource property, or when you construct a new Output using functions like pulumi.Output.format or pulumi.Output.concat, you are not performing the operation immediately. Instead, you are building a program that describes how to compute the final value. Pulumi’s engine then executes this program at the right time, ensuring that values are available when needed and that dependencies are resolved correctly. This is what allows Pulumi to handle complex inter-stack relationships without manual orchestration.
The next step is understanding how to manage secrets when sharing values between stacks, as directly exporting sensitive information like passwords is a security risk.