Pulumi Configuration Ecosystem and Environment Variable Architecture

The Pulumi infrastructure-as-code (IaC) framework utilizes a multi-layered configuration system designed to decouple deployment logic from environment-specific data. This architecture encompasses a wide array of environment variables that govern the behavior of the Pulumi CLI, the management of state backends, and the operational parameters of self-hosted Enterprise deployments. By leveraging a hierarchical priority system, Pulumi ensures that developers can override settings at various stages of the deployment lifecycle, from local development environments to highly secure production pipelines.

Pulumi Self-Hosted Infrastructure Configuration

When deploying Pulumi as a self-hosted entity, the environment variables act as the primary mechanism for service orchestration and dependency injection. These variables are critical for the API service, the Console service, and the underlying database and storage backends. The flow of these variables typically originates from configuration files or shell environments and is injected into the running services, often via containerization tools.

The core operational requirements for a self-hosted deployment are centered around licensing and network identity. The PULUMI_LICENSE_KEY is a mandatory variable required by the API service. Without this key, the system cannot validate the entitlement for the software, and the run-ee.sh script used in quickstart Docker Compose deployments performs a specific validation check on lines 22-25 to ensure the key is present.

Enterprise functionality is toggled via the PULUMI_ENTERPRISE variable. By default, this is set to "true", which enables the full suite of enterprise-grade features within the API. Network routing is managed through domain variables, which are essential for services like SAML SSO to function correctly. The PULUMI_API_DOMAIN specifies the external domain for the API service (defaulting to localhost:8080 in Docker Compose), while the PULUMI_CONSOLE_DOMAIN identifies the domain for the Console service (defaulting to localhost:3000).

Self-Hosted Deployment Scenarios and Variable Dependencies

Depending on the target environment—whether it be a local development machine or a production cloud environment—the required variables change significantly. This is due to the shift from local file-system storage to remote blob storage and centralized key management.

Minimal Docker Compose Deployment

For users implementing a minimal deployment for testing or small-scale internal use, the system relies on local storage and encryption. In this scenario, the only manual requirement is the PULUMI_LICENSE_KEY. The run-ee.sh script automatically generates a set of internal variables to handle data persistence.

  • PULUMIDATAPATH: Points to $HOME/pulumi-self-hosted-installers/data.
  • PULUMILOCALKEYS: Points to $PULUMI_DATA_PATH/localkeys.
  • PULUMILOCALOBJECTS: Points to $PULUMI_DATA_PATH/checkpoints.
  • PULUMIPOLICYPACKLOCALHTTP_OBJECTS: Points to $PULUMI_DATA_PATH/policypacks.
  • PULUMIDATABASEENDPOINT: Set to pulumi-db:3306.
  • PULUMISEARCHDOMAIN: Set to http://opensearch:9200.

Production AWS Deployment

Moving to a production AWS environment requires the abandonment of local storage in favor of S3 and AWS KMS. This transition ensures that state checkpoints and policy packs are durable and that secrets are encrypted using industry-standard hardware security modules.

Required variables for AWS production:
- PULUMILICENSEKEY: Valid license key.
- PULUMICHECKPOINTBLOBSTORAGEENDPOINT: Format s3://bucket-name.
- PULUMIPOLICYPACKBLOBSTORAGEENDPOINT: Format s3://bucket-name/policypacks.
- PULUMI
KMSKEY: The ARN of the AWS KMS key (e.g., arn:aws:kms:us-west-2:123456789012:key/abc...).
- AWS
REGION: The AWS region where resources are hosted.
- AWSACCESSKEYID or AWSROLEARN: For identity and access management.
- AWS
SECRETACCESSKEY: Required if using access keys for authentication.

In this configuration, the variables PULUMI_LOCAL_OBJECTS, PULUMI_POLICY_PACK_LOCAL_HTTP_OBJECTS, and PULUMI_LOCAL_KEYS must not be set, as they would conflict with the remote storage backend.

Production Azure Deployment

For Azure-based production environments, the system utilizes Azure Blob Storage and Azure Key Vault.

Required variables for Azure production:
- PULUMILICENSEKEY: Valid license key.
- PULUMICHECKPOINTBLOBSTORAGEENDPOINT: Format azblob://container-name.

Pulumi CLI Operational Environment Variables

The Pulumi CLI provides a vast array of environment variables that allow users to modify the behavior of the tool without altering the codebase or command-line arguments for every execution. These variables range from authentication and backend selection to deep-level debugging and performance tuning.

Stack and Backend Management

One of the most critical variables is PULUMI_STACK, which allows a user to specify the selected stack. This variable overrides the selection made via the pulumi stack select STACK command. However, the hierarchy of priority is strict: the --stack command line flag takes the highest priority, followed by PULUMI_STACK, and finally the stack previously selected via the CLI.

Backend routing is handled by PULUMI_BACKEND_URL. This variable allows the CLI to bypass the default backend (typically Pulumi Cloud) and connect to a specific URL, such as an S3 bucket (e.g., PULUMI_BACKEND_URL="s3://your-pulumi-state-bucket"). For authentication, PULUMI_ACCESS_TOKEN can be used to bypass the interactive pulumi login prompt by providing the token directly in the environment.

Configuration and Secrets

The PULUMI_CONFIG variable is a specialized input that is ignored during standard operations like up or preview. If it is provided, it must be a valid JSON string, such as PULUMI_CONFIG='{"project:myTag":"val1","project:mySecret":"val2"}'.

For the protection of sensitive data, PULUMI_CONFIG_PASSPHRASE is utilized. This variable is used to unlock configuration values and secrets. The passphrase generates a unique key for the stack, and subsequent encryption of configuration and state values is performed using the AES-256-GCM algorithm.

Debugging and Development

For developers troubleshooting the Pulumi engine or the communication between the CLI and the API, several debug variables are available:

  • PULUMIDEBUGGRPC: This should be set to the path of a log file (e.g., /path/to/grpc-debug.log) to capture gRPC debug traces.
  • PULUMIDEBUGPROMISE_LEAKS: Set to true to receive verbose error messages regarding promise leaks, an improvement introduced in v0.12.2.
  • PULUMI_DEV: Enables internal features used for hacking on the Pulumi codebase.
  • PULUMIDISABLEVALIDATION: Disables the format validation of system inputs.

Performance and Plugin Control

Pulumi allows for the optimization of resource operations and the control of how plugins are acquired:

  • PULUMI_PARALLEL: Controls the number of resource operations that can run in parallel. A value of 1 disables parallelism.
  • PULUMIPARALLELDIFF: Set to true to enable parallel diff calculations.
  • PULUMIDISABLEAUTOMATICPLUGINACQUISITION: Set to true to prevent Pulumi from automatically downloading missing plugins.
  • PULUMIIGNOREAMBIENT_PLUGINS: Set to true to stop the CLI from searching the $PATH for additional plugins.
  • PULUMIPLUGINDOWNLOADURLOVERRIDES: Allows the redirection of plugin downloads using a regexp=URL format (e.g., PULUMI_PLUGIN_DOWNLOAD_URL_OVERRIDES="^https://foo=https://bar,^github://=https://buzz").

Language and Environment Specifics

The CLI also manages how it interacts with the underlying runtimes:

  • PULUMIPYTHONCMD: As of v0.16.6, the default is python3. Users can set this to a specific binary version.
  • PULUMIPREFERYARN: Set to true to use yarn instead of npm for Node.js dependencies.
  • PULUMI_HOME: Overrides the default artifact storage location (usually ~/.pulumi).
  • PULUMIRUNPROGRAM: Equivalent to the --run-program=true flag, used during refresh and destroy operations.
  • PULUMISKIPCHECKPOINTS: Introduced in v3.40.1, this allows the system to skip saving intermediate state checkpoints and only save the final deployment state.

Pulumi Config System and Programmatic Access

Beyond environment variables, Pulumi employs a configuration system based on YAML files (e.g., Pulumi.dev.yaml). This system allows for the definition of values that can be read directly within the Pulumi program.

Reading Configuration in Code

Configuration values are typically namespaced to avoid collisions. For example, if a value is set via pulumi config set aws:region us-west-2, it is stored in the YAML file under the aws namespace.

The following table demonstrates how to access these values across different supported languages:

Language Implementation Pattern
TypeScript/JavaScript let awsConfig = new pulumi.Config("aws"); let awsRegion = awsConfig.require("region");
Python aws_config = pulumi.Config("aws"); aws_region = aws_config.require("region")
Go awsConfig := config.New(ctx, "aws"); awsRegion := awsConfig.Require("region")
C# var awsConfig = new Pulumi.Config("aws"); var awsRegion = awsConfig.Require("region");

Component-Level Configuration

When building reusable libraries or custom components, it is a best practice to avoid using a global configuration namespace. Instead, the library's name should be passed to the Config class. This ensures that if multiple instances of a component are used, their configurations remain isolated.

Example for a custom component:
typescript class MyComponent extends pulumi.ComponentResource { constructor(name: string, args = {}, opts: pulumi.ComponentResourceOptions = {}) { super("mylib:index:MyComponent", name, args, opts); // Read settings from the 'mylib' namespace (e.g., 'mylib:name'). const config = new pulumi.Config("mylib"); const name = config.require("name"); } }

Cloud Provider Integration and Secrets

Pulumi integrates deeply with cloud provider credentials, allowing for flexibility between using environment variables or Pulumi's internal secret management.

Azure Service Principal Configuration

For Azure deployments, credentials can be provided through system environment variables or through the Pulumi config system using the --secret flag to ensure the values are encrypted at rest.

Environment variables for Azure:
- ARMCLIENTID
- ARMCLIENTSECRET
- ARMTENANTID
- ARMSUBSCRIPTIONID

Alternatively, these can be set as encrypted configuration:
- pulumi config set azure:clientId <clientID> --secret
- pulumi config set azure:clientSecret <clientSecret> --secret
- pulumi config set azure:tenantId <tenantID> --secret
- pulumi config set azure:subscriptionId <subscriptionId> --secret

Programmatic Retrieval of Environment Variables

In some scenarios, a developer may need to retrieve a system environment variable directly within the code rather than using pulumi.Config. This is common in CI/CD pipelines like Azure DevOps where the agent VM provides the credentials.

Example in C#:
csharp const string EnvVarKeyTenantId = "AZURE_TENANT_ID"; var tenantId = Environment.GetEnvironmentVariable(EnvVarKeyTenantId, EnvironmentVariableTarget.Process);
In this example, EnvironmentVariableTarget.Process is used to ensure the variable is read from the current process. While .Machine is available for Windows, it may not be applicable in all environment types.

Comprehensive Analysis of Variable Hierarchies and Impacts

The interaction between environment variables, CLI flags, and configuration files creates a complex priority matrix. Understanding this matrix is essential for avoiding "configuration drift," where the deployed infrastructure does not match the expected state due to an unnoticed override.

The highest priority is always given to the explicit command-line flag. This allows a DevOps engineer to perform a one-time override during an emergency deployment without changing the underlying configuration files. The second level of priority is the environment variable (e.g., PULUMI_STACK), which is ideal for CI/CD pipelines where the environment determines the target stack. The final level of priority is the persisted configuration file (e.g., Pulumi.dev.yaml), which serves as the baseline for the environment.

The impact of this architecture is most visible in security and state management. By utilizing PULUMI_CONFIG_PASSPHRASE and the --secret flag, Pulumi implements a defense-in-depth strategy. Secrets are not stored in plain text in the state file; instead, they are encrypted using AES-256-GCM. This means that even if an attacker gains access to the state file, they cannot decrypt the secrets without the corresponding passphrase or KMS key.

Furthermore, the separation of PULUMI_CHECKPOINT_BLOB_STORAGE_ENDPOINT and PULUMI_POLICY_PACK_BLOB_STORAGE_ENDPOINT in production environments allows organizations to apply different access control policies (IAM roles) to state data versus policy-as-code definitions. This prevents a scenario where a user with permission to update policy packs might inadvertently have access to the sensitive state of the entire infrastructure.

Sources

  1. Pulumi Self-Hosted Installers Environment Variables Reference
  2. Pulumi CLI Environment Variables Official Documentation
  3. Pulumi Configuration Concepts
  4. Hovermind Pulumi Environment Variables Guide

Related Posts