A Pulumi provider for managing DevZero infrastructure resources.
Project description
pulumi-devzero
The official Python Pulumi provider for DevZero — manage clusters, workload policies, and node policies as code.
Installation
pip install pulumi-devzero
Requires: pulumi>=3.0.0,<4.0.0, Python 3.9+
Configuration
Before using the DevZero Pulumi provider, configure your credentials using Pulumi config.
1. Generate a Personal Access Token (PAT)
Go to the DevZero user settings page to generate your PAT token:
https://www.devzero.io/settings/user-settings/general
Create a Personal Access Token and copy it.
2. Find your Team ID
You can find your DevZero Team ID in the organization settings:
https://www.devzero.io/settings/organization-settings/account
Copy the Team ID value from this page.
3. Set Pulumi configuration
Run the following commands in your Pulumi project:
pulumi config set --secret devzero:token <YOUR_PAT_TOKEN>
pulumi config set devzero:teamId <TEAM_ID>
pulumi config set devzero:url https://dakr.devzero.io # optional, this is the default
Example
pulumi config set --secret devzero:token dz_pat_xxxxxxxxxxxxx
pulumi config set devzero:teamId team_123456789
The --secret flag ensures that your token is encrypted in the Pulumi state.
Quick Start
import pulumi
import pulumi_devzero as devzero
# 1. Create a cluster
cluster = devzero.resources.Cluster("prod-cluster",
name="prod-cluster",
)
# 2. Create a workload policy with CPU vertical scaling
policy = devzero.resources.WorkloadPolicy("cpu-scaling-policy",
name="cpu-scaling-policy",
description="Policy with CPU vertical scaling enabled",
cpu_vertical_scaling=devzero.resources.VerticalScalingArgsArgs(
enabled=True,
target_percentile=0.95,
min_request=50,
max_request=4000,
max_scale_up_percent=100,
max_scale_down_percent=25,
overhead_multiplier=1.1,
limits_adjustment_enabled=True,
limit_multiplier=1.5,
),
)
# 3. Apply the policy to the cluster for all Deployments
target = devzero.resources.WorkloadPolicyTarget("prod-cluster-target",
name="prod-cluster-deployments-target",
description="Apply cpu-scaling-policy to all Deployments in prod-cluster",
policy_id=policy.id,
cluster_ids=[cluster.id],
kind_filter=["Deployment"],
enabled=True,
)
pulumi.export("cluster_id", cluster.id)
pulumi.export("cluster_token", pulumi.Output.secret(cluster.token))
pulumi.export("policy_id", policy.id)
pulumi.export("target_id", target.id)
pulumi up
Resources
Cluster
Provision and manage a DevZero cluster.
import pulumi
import pulumi_devzero as devzero
cluster = devzero.resources.Cluster("my-cluster",
name="my-cluster",
)
pulumi.export("id", cluster.id)
pulumi.export("token", pulumi.Output.secret(cluster.token))
WorkloadPolicy
Configure vertical and horizontal scaling policies for workloads.
import pulumi_devzero as devzero
policy = devzero.resources.WorkloadPolicy("my-policy",
name="my-policy",
description="Vertical scaling for CPU and memory",
enable_pmax_protection=True,
pmax_ratio_threshold=3.0,
cpu_vertical_scaling=devzero.resources.VerticalScalingArgsArgs(
enabled=True,
target_percentile=0.95,
min_request=50,
max_request=4000,
max_scale_up_percent=100,
max_scale_down_percent=25,
overhead_multiplier=1.1,
limits_adjustment_enabled=True,
limit_multiplier=1.5,
adjust_req_even_if_not_set=True,
limits_removal_enabled=False,
),
memory_vertical_scaling=devzero.resources.VerticalScalingArgsArgs(
enabled=True,
target_percentile=0.9,
min_request=128,
max_request=8192,
max_scale_up_percent=50,
max_scale_down_percent=20,
overhead_multiplier=1.2,
limits_adjustment_enabled=True,
limit_multiplier=1.3,
adjust_req_even_if_not_set=True,
limits_removal_enabled=False,
),
)
VerticalScalingArgsArgs fields:
| Field | Type | Description |
|---|---|---|
enabled |
bool |
Enable this scaling axis |
target_percentile |
float |
Percentile of observed usage to target (e.g. 0.95) |
min_request |
int |
Minimum resource request (millicores / MiB) |
max_request |
int |
Maximum resource request (millicores / MiB) |
max_scale_up_percent |
float |
Max % to scale up in one step. Default: 1000 |
max_scale_down_percent |
float |
Max % to scale down in one step. Default: 1.0 |
overhead_multiplier |
float |
Multiplier added on top of the recommendation |
limits_adjustment_enabled |
bool |
Whether to also adjust resource limits |
limit_multiplier |
float |
Limits = request × limit_multiplier |
min_data_points |
int |
Minimum data points before a recommendation is emitted. Default: 20 |
adjust_req_even_if_not_set |
bool |
Recommend requests even when the workload has no existing requests set. Default: false |
limits_removal_enabled |
bool |
Actively remove limits from workloads (CPU axis only — memory limits removal is not supported). Takes precedence over limits_adjustment_enabled. Default: false |
WorkloadPolicy pmax & VPA knob fields:
| Field | Type | Description |
|---|---|---|
enable_pmax_protection |
bool |
Raise requests to cover peak usage when max/recommendation ratio exceeds pmax_ratio_threshold. Default: false |
pmax_ratio_threshold |
float |
Max-to-recommendation ratio that triggers pmax protection. Default: 3.0 |
loopback_period_seconds |
int |
Look-back period in seconds for usage data. Default: 86400 (24 h) |
min_data_points |
int |
Global minimum data points for recommendations. Default: 15 |
min_change_percent |
float |
Global minimum change threshold. Default: 0.2 (20%) |
min_vpa_window_data_points |
int |
Minimum data points in VPA analysis window. Default: 30 |
cooldown_minutes |
int |
Minutes between applying recommendations. Default: 300 (5 h) |
WorkloadPolicyTarget
Apply a workload policy to one or more clusters with optional filters.
import pulumi_devzero as devzero
target = devzero.resources.WorkloadPolicyTarget("my-target",
name="my-target",
policy_id=policy.id,
cluster_ids=[cluster.id],
kind_filter=["Deployment", "StatefulSet"],
namespace_selector=devzero.resources.LabelSelectorArgsArgs(match_labels={"env": "production"}),
enabled=True,
)
Fields:
| Field | Type | Description |
|---|---|---|
name |
str |
Unique target name |
policy_id |
str |
ID of the WorkloadPolicy to apply |
cluster_ids |
list[str] |
Cluster IDs to target |
description |
str |
Human-readable description (optional) |
priority |
int |
Evaluation priority; higher value wins when targets overlap |
kind_filter |
list[str] |
Pod | Deployment | StatefulSet | DaemonSet | Job | CronJob | ReplicaSet | ReplicationController | Rollout |
workload_names |
list[str] |
Explicit list of workload names to include |
node_group_names |
list[str] |
Restrict matching to specific node groups by name |
name_pattern |
NamePatternArgsArgs |
Regex pattern to match workload names |
namespace_selector |
LabelSelectorArgsArgs |
Select namespaces by labels (match_labels / match_expressions) |
workload_selector |
LabelSelectorArgsArgs |
Select workloads by labels |
enabled |
bool |
Activate the target |
NodePolicy
Configure node provisioning and pooling (AWS / Azure) using Karpenter under the hood.
import pulumi_devzero as devzero
node_policy = devzero.resources.NodePolicy("my-node-policy",
name="my-node-policy",
description="AWS node policy with on-demand and spot capacity",
weight=10,
capacity_types=devzero.resources.LabelSelectorArgsArgs(match_expressions=[devzero.resources.LabelSelectorRequirementArgsArgs(key="<label-key>", operator="In", values=["<value>"])]),
instance_categories=devzero.resources.LabelSelectorArgsArgs(match_labels={"<label-key>": "<label-value>"}),
labels={"environment": "production"},
taints=[devzero.resources.TaintArgsArgs(key="dedicated", value="gpu", effect="NoSchedule")],
disruption=devzero.resources.DisruptionPolicyArgsArgs(
consolidation_policy="WhenEmptyOrUnderutilized",
consolidate_after="30s",
expire_after="720h",
),
limits=devzero.resources.ResourceLimitsArgsArgs(cpu="1000", memory="1000Gi"),
aws=devzero.resources.AWSNodeClassSpecArgsArgs(
ami_family="AL2",
role="KarpenterNodeRole",
subnet_selector_terms=[devzero.resources.SubnetSelectorTermArgsArgs(tags={"karpenter.sh/discovery": "my-cluster"})],
security_group_selector_terms=[devzero.resources.SecurityGroupSelectorTermArgsArgs(tags={"karpenter.sh/discovery": "my-cluster"})],
block_device_mappings=[devzero.resources.BlockDeviceMappingArgsArgs(
device_name="/dev/xvda",
root_volume=True,
ebs=devzero.resources.BlockDeviceArgsArgs(volume_size="100Gi", volume_type="gp3", encrypted=True),
)],
),
)
NodePolicyArgs fields:
| Field | Type | Description |
|---|---|---|
name |
str |
Unique policy name |
description |
str |
Human-readable description |
weight |
int |
Priority weight when multiple policies match (higher = preferred) |
instance_categories |
LabelSelectorArgsArgs |
Filter by instance category letter: e.g. m, c, r (AWS) or D, E (Azure) |
instance_families |
LabelSelectorArgsArgs |
Filter instance families (e.g. c5, m5) |
instance_cpus |
LabelSelectorArgsArgs |
Filter by vCPU count |
instance_sizes |
LabelSelectorArgsArgs |
Filter instance sizes (e.g. large, xlarge) |
instance_types |
LabelSelectorArgsArgs |
Explicit instance types (e.g. m5.xlarge) |
instance_generations |
LabelSelectorArgsArgs |
Filter by instance generation |
instance_hypervisors |
LabelSelectorArgsArgs |
Filter by hypervisor type |
zones |
LabelSelectorArgsArgs |
Availability zones to provision into |
architectures |
LabelSelectorArgsArgs |
CPU architectures (e.g. amd64, arm64) |
capacity_types |
LabelSelectorArgsArgs |
Capacity types: on-demand | spot | reserved |
operating_systems |
LabelSelectorArgsArgs |
OS filter (e.g. linux, windows) |
labels |
dict[str, str] |
Labels applied to provisioned nodes |
taints |
list[TaintArgsArgs] |
Taints applied to provisioned nodes |
disruption |
DisruptionPolicyArgsArgs |
Node disruption / consolidation settings |
limits |
ResourceLimitsArgsArgs |
Max total CPU/memory this policy may provision |
node_pool_name |
str |
Override the Karpenter NodePool name |
node_class_name |
str |
Override the Karpenter NodeClass name |
aws |
AWSNodeClassSpecArgsArgs |
AWS-specific node class configuration |
azure |
AzureNodeClassSpecArgsArgs |
Azure-specific node class configuration |
raw |
list[RawKarpenterSpecArgsArgs] |
Raw Karpenter YAML (escape hatch) |
RawKarpenterSpecArgsArgs fields:
| Field | Type | Description |
|---|---|---|
nodepool_yaml |
str |
Raw YAML for a complete Karpenter NodePool resource |
nodeclass_yaml |
str |
Raw YAML for a complete Karpenter NodeClass resource |
DisruptionPolicyArgsArgs fields:
| Field | Type | Description |
|---|---|---|
consolidation_policy |
str |
WhenEmpty | WhenEmptyOrUnderutilized |
consolidate_after |
str |
Wait time after node is empty before consolidating (e.g. 30s) |
expire_after |
str |
Force-replace nodes after this duration (e.g. 720h) |
ttl_seconds_after_empty |
int |
Seconds before an empty node is terminated (deprecated; prefer consolidate_after) |
termination_grace_period_seconds |
int |
Grace period before forcefully terminating a draining node |
budgets |
list[DisruptionBudgetArgsArgs] |
Limits on how many nodes may be disrupted at once |
AWSNodeClassSpecArgsArgs fields:
| Field | Type | Description |
|---|---|---|
ami_family |
str |
AMI family: AL2, AL2023, Bottlerocket, Windows2019, Windows2022 |
role |
str |
IAM role name for nodes (Karpenter creates the instance profile) |
instance_profile |
str |
IAM instance profile name (alternative to role) |
subnet_selector_terms |
list[SubnetSelectorTermArgsArgs] |
Subnet selectors (by tag or ID) |
security_group_selector_terms |
list[SecurityGroupSelectorTermArgsArgs] |
Security group selectors |
capacity_reservation_selector_terms |
list[CapacityReservationSelectorTermArgsArgs] |
EC2 capacity reservation selectors |
ami_selector_terms |
list[AMISelectorTermArgsArgs] |
AMI selectors (by alias, tag, or ID) |
block_device_mappings |
list[BlockDeviceMappingArgsArgs] |
EBS volume configuration |
instance_store_policy |
str |
NVMe instance store policy. Value: INSTANCE_STORE_POLICY_RAID0 |
tags |
dict[str, str] |
AWS tags on all provisioned resources |
associate_public_ip_address |
bool |
Assign a public IP to nodes |
detailed_monitoring |
bool |
Enable CloudWatch detailed monitoring |
metadata_options |
MetadataOptionsArgsArgs |
EC2 IMDS options (IMDSv2, hop limit, etc.) |
kubelet |
KubeletConfigurationArgsArgs |
Kubelet overrides (max_pods, eviction thresholds, etc.) |
user_data |
str |
Custom launch template user data |
context |
str |
Additional EC2 launch template context ARN for advanced customization |
AzureNodeClassSpecArgsArgs fields:
| Field | Type | Description |
|---|---|---|
vnet_subnet_id |
str |
Azure VNet subnet resource ID |
image_family |
str |
Image family: AzureLinux, Ubuntu2204, etc. |
os_disk_size_gb |
int |
OS disk size in GB |
fips_mode |
str |
Enabled | Disabled |
max_pods |
int |
Max pods per node |
tags |
dict[str, str] |
Azure tags on provisioned resources |
kubelet |
AzureKubeletConfigurationArgsArgs |
Kubelet overrides for Azure nodes |
NodePolicyTarget
Apply a node policy to one or more clusters.
import pulumi_devzero as devzero
node_policy_target = devzero.resources.NodePolicyTarget("my-node-policy-target",
name="my-node-policy-target",
policy_id=node_policy.id,
cluster_ids=[cluster.id],
enabled=True,
)
Fields:
| Field | Type | Description |
|---|---|---|
name |
str |
Unique target name |
policy_id |
str |
ID of the NodePolicy to apply |
cluster_ids |
list[str] |
Cluster IDs to target. At most 1 entry — the backend rejects more than one. |
description |
str |
Human-readable description (optional) |
enabled |
bool |
Activate the target |
Note:
pulumi destroyremoves this resource from Pulumi state but does not delete it on the DevZero backend — no delete RPC exists for NodePolicyTarget.
Data Sources
get_cluster_id_by_name
Look up an existing cluster by name and return its ID. Use this when a cluster was registered manually (not created by Pulumi) and you need its ID to attach policies or inject into values.yaml / a Kubernetes secret.
import pulumi
import pulumi_devzero as devzero
existing = devzero.resources.get_cluster_id_by_name(
name="my-existing-cluster",
# team_id is optional — defaults to devzero:teamId from provider config
# region="us-east-1", # optional: filter by region
# cloud_provider="AWS", # optional: AWS | GCP | AKS | OCI
# liveness="PREFER_LIVE", # optional: IGNORE | PREFER_LIVE | REQUIRE_LIVE
)
# Attach a policy to the existing cluster
target = devzero.resources.WorkloadPolicyTarget("my-target",
name="my-target",
policy_id=policy.id,
cluster_ids=[existing.cluster_id],
kind_filter=["Deployment"],
enabled=True,
)
pulumi.export("existing_cluster_id", existing.cluster_id)
Inputs:
| Field | Type | Required | Description |
|---|---|---|---|
name |
str |
yes | Cluster name to look up |
team_id |
str |
no | Defaults to devzero:teamId from provider config |
region |
str |
no | Filter by region name (e.g. us-east-1) |
cloud_provider |
str |
no | Filter by cloud provider: AWS | GCP | AKS | OCI |
liveness |
str |
no | IGNORE (default) | PREFER_LIVE | REQUIRE_LIVE |
Outputs:
| Field | Type | Description |
|---|---|---|
cluster_id |
str |
UUID of the matching cluster |
Note: If multiple clusters share the same name, the newest one is returned by default. Use
liveness="PREFER_LIVE"or"REQUIRE_LIVE"to filter by heartbeat freshness.
Destroying Resources
# Tear down all resources in the stack
pulumi destroy
# Remove the stack itself
pulumi stack rm <stack-name>
Links
License
MIT — Copyright (c) 2026 DevZero Inc.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pulumi_devzero-0.1.11.tar.gz.
File metadata
- Download URL: pulumi_devzero-0.1.11.tar.gz
- Upload date:
- Size: 55.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef09ace6777212a36900de08960ecc5a192c865fb04641ca45af882995561d4d
|
|
| MD5 |
3701abf785a48b1e4584ef9b7d3f97ab
|
|
| BLAKE2b-256 |
a05f7a531d877da43e5a1be281a403262d03966b7f9430c5a44d932bae53e19c
|
File details
Details for the file pulumi_devzero-0.1.11-py3-none-any.whl.
File metadata
- Download URL: pulumi_devzero-0.1.11-py3-none-any.whl
- Upload date:
- Size: 59.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
968367a0428a6c117ed1700dd1cea8813fe4928274df00d1b058994234bb9935
|
|
| MD5 |
046684ac0d1894c6cf998e9cf156f54b
|
|
| BLAKE2b-256 |
7de6c6f7ec5148d2317203d231714d98c170d29f8b0a50f7880211532b9a62d6
|