ax-check.com
by Gauge
P
Pulumi
pulumi.com
ax-check.com
Checked 29 September 2026
A
100/100
Agents haven’t verified setup
Pulumi's docs, install steps, and pricing are all clear upfront.
All 23 checked items passed, including install, API/MCP docs, and pricing. Every plan price is stated plainly with no login required.
1 fix to start with
Concrete changes, backed by what we found.
Recorded coding sessions
Same task, 3 models, no pulumi.com account. Recorded 29 September 2026 on Pi.
3 recorded · 5m 57s
5m 57s · recorded
Session findings
Select a finding to see it in context.
›Thinking…
The user wants a simple Pulumi example, pricing explanation, and whether it's manageable for me. Let me check the environment first.
Pulumi does have a developer workflow (SDK/CLI), so it's actionable. But it requires cloud credentials to actually deploy. Let me check what's available in the sandbox.
Let me investigate.
I'll check the environment first.
✓$ cd /sandbox/repo && ls -la && echo "---" && which pulumi python3 node npm pip3 2>&1 && echo "---" && python3 -V 2>&1; node -v 2>&1✓└{"content":[{"text":"total 16\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 7 root root 4096 Sep 29 08:18 ..\ndrwxr-xr-x 8 root root 4096 Sep 24 20:05…ctrl+o
{"content":[{"text":"total 16\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 7 root root 4096 Sep 29 08:18 ..\ndrwxr-xr-x 8 root root 4096 Sep 24 20:05 .git\n-rw-r--r-- 1 root root 408 Sep 18 08:00 .gitignore\n---\n/usr/local/bin/python3\n/usr/local/bin/node\n/usr/local/bin/npm\n/usr/local/bin/pip3\nv22.23.2\n","type":"text"}]}✓$ cd /sandbox/repo && git log --oneline -5 2>&1 | head; echo "--- env ---"; env | grep -iE "pulumi|aws|azure|google|cloud|token" | sed 's/=.*/=<set>/' ; echo "--- net ---"; curl -s -m 8 -o /dev/null -w …✓└{"content":[{"text":"9ebf5fb Initial workspace\n--- env ---\nAWS_CA_BUNDLE=<set>\n--- net ---\n200\n","type":"text"}]}ctrl+o
cd /sandbox/repo && git log --oneline -5 2>&1 | head; echo "--- env ---"; env | grep -iE "pulumi|aws|azure|google|cloud|token" | sed 's/=.*/=<set>/' ; echo "--- net ---"; curl -s -m 8 -o /dev/null -w "%{http_code}\n" https://www.pulumi.com 2>&1
›Thinking…
Empty repo. Pulumi CLI not installed. Network works. Pulumi has a developer workflow — SDK exists. But actual deployment requires cloud credentials (AWS/Azure/GCP) or Pulumi Cloud state backend. Let me check whether we can install pulumi and whether pulumi cloud login is possible (no token).
Key: Pulumi without credentials can still be used with a local backend (--backend file://~) and mock/preview... but "preview" still needs provider credentials for cloud resources. However, there are providers that don't need credentials: e.g., random, tls, local/command, time. Those are real Pulumi providers that work fully offline-ish (need plugin download). That gives a genuine end-to-end up without any cloud account or long-running services.
Let me try installing pulumi CLI and see if plugin downloads work. That's the plan: a random + tls example using local file backend. That's light: no local service stacks, no long-running commands.
Let me check whether pulumi CLI can be installed quickly.
Actually first check: does pip have pulumi? pip install pulumi pulumi-random would be simplest — the Pulumi CLI is needed for engine though. Let me check.
Let's try downloading pulumi CLI binary.
Empty repo, network is up, but no Pulumi CLI. Pulumi does have a real developer workflow (SDK + CLI), so I'll proceed. Let me check installability.
✓$ cd /tmp && timeout 90 curl -fsSL https://get.pulumi.com -o get.pulumi.com 2>&1 && head -20 get.pulumi.com && echo "OK-DOWNLOAD"✓└{"content":[{"text":"#!/bin/sh\nset -e\n\nRESET=\"\\\\033[0m\"\nRED=\"\\\\033[31;1m\"\nGREEN=\"\\\\033[32;1m\"\nYELLOW=\"\\\\033[33;1m\"\nBLUE=\"\\\\033[34;1m\"…ctrl+o
{"content":[{"text":"#!/bin/sh\nset -e\n\nRESET=\"\\\\033[0m\"\nRED=\"\\\\033[31;1m\"\nGREEN=\"\\\\033[32;1m\"\nYELLOW=\"\\\\033[33;1m\"\nBLUE=\"\\\\033[34;1m\"\nWHITE=\"\\\\033[37;1m\"\n\nprint_unsupported_platform()\n{\n >&2 say_red \"error: We're sorry, but it looks like Pulumi is not supported on your platform\"\n >&2 say_red \" We support 64-bit versions of Linux and macOS and are interested in supporting\"\n >&2 say_red \" more platforms. Please open an issue at https://github.com/pulumi/pulumi and\"\n >&2 say_red \" let us know what platform you're using!\"\n}\n\nsay_green()\n{\nOK-DOWNLOAD\n","type":"text"}]}✓$ timeout 60 pip3 index versions pulumi 2>&1 | head -5; echo "---"; timeout 60 npm view pulumi version 2>&1 | head -3✓└{"content":[{"text":"pulumi (3.265.0)\nAvailable versions: 3.265.0, 3.264.0, 3.263.0, 3.262.0, 3.261.0, 3.260.0, 3.259.0, 3.258.0, 3.257.0, 3.256.0, 3.255.0, 3.…ctrl+o
{"content":[{"text":"pulumi (3.265.0)\nAvailable versions: 3.265.0, 3.264.0, 3.263.0, 3.262.0, 3.261.0, 3.260.0, 3.259.0, 3.258.0, 3.257.0, 3.256.0, 3.255.0, 3.254.0, 3.253.0, 3.252.0, 3.251.0, 3.250.0, 3.249.0, 3.248.0, 3.247.0, 3.246.0, 3.245.0, 3.244.0, 3.243.0, 3.242.0, 3.241.0, 3.239.0, 3.238.0, 3.237.0, 3.236.0, 3.235.0, 3.234.0, 3.233.0, 3.232.0, 3.231.0, 3.230.0, 3.229.0, 3.228.0, 3.227.0, 3.226.0, 3.225.1, 3.225.0, 3.224.0, 3.223.0, 3.222.0, 3.221.0, 3.220.0, 3.219.0, 3.218.0, 3.217.1, 3.217.0, 3.216.0, 3.215.0, 3.214.1, 3.214.0, 3.213.0, 3.212.0, 3.211.0, 3.210.0, 3.209.0, 3.208.0, 3.207.0, 3.206.0, 3.205.0, 3.204.0, 3.203.0, 3.202.0, 3.201.0, 3.200.0, 3.199.0, 3.198.0, 3.197.0, 3.196.0, 3.195.0, 3.194.0, 3.193.0, 3.192.0, 3.191.0, 3.190.0, 3.189.0, 3.188.0, 3.187.0, 3.186.0, 3.185.0, 3.184.0, 3.183.0, 3.182.0, 3.181.0, 3.180.0, 3.178.0, 3.177.0, 3.176.0, 3.175.0, 3.174.0, 3.173.0, 3.172.0, 3.171.0, 3.170.0, 3.169.0, 3.168.0, 3.167.0, 3.166.0, 3.165.0, 3.163.0, 3.162.0, 3.161.0, 3.160.0, 3.159.0, 3.158.0, 3.157.0, 3.156.0, 3.155.0, 3.154.0, 3.153.1, 3.153.0, 3.152.0, 3.151.0, 3.150.0, 3.149.0, 3.148.0, 3.147.0, 3.146.0, 3.145.0, 3.144.1, 3.144.0, 3.143.0, 3.142.0, 3.141.0, 3.140.0, 3.139.0, 3.138.0, 3.137.0, 3.136.1, 3.136.0, 3.135.1, 3.135.0, 3.134.1, 3.133.0, 3.132.0, 3.131.0, 3.130.0, 3.129.0, 3.128.0, 3.127.0, 3.126.0, 3.125.0, 3.124.0, 3.123.0, 3.122.0, 3.121.0, 3.120.0, 3.119.0, 3.118.0, 3.117.0, 3.116.1, 3.116.0, 3.115.2, 3.115.1, 3.115.0, 3.114.0, 3.113.3, 3.113.2, 3.113.1, 3.113.0, 3.112.0, 3.111.1, 3.111.0, 3.110.0, 3.109.0, 3.108.1, 3.108.0, 3.107.0, 3.106.0, 3.105.0, 3.104.2, 3.104.1, 3.104.0, 3.103.1, 3.103.0, 3.102.0, 3.101.1, 3.101.0, 3.100.0, 3.99.0, 3.98.0, 3.97.0, 3.96.2, 3.96.1, 3.96.0, 3.95.0, 3.94.2, 3.94.1, 3.94.0, 3.93.0, 3.92.0, 3.91.1, 3.91.0, 3.90.1, 3.90.0, 3.89.0, 3.88.1, 3.88.0, 3.87.0, 3.86.0, 3.85.0, 3.84.0, 3.83.0, 3.82.1, 3.82.0, 3.81.0, 3.80.0, 3.79.0, 3.78.1, 3.78.0, 3.77.1, 3.77.0, 3.76.1, 3.76.0, 3.75.0, 3.74.0, 3.73.0, 3.72.2, 3.72.1, 3.72.0, 3.71.0, 3.70.0, 3.69.0, 3.68.0, 3.67.1, 3.67.0, 3.66.0, 3.65.1, 3.65.0, 3.64.0, 3.63.0, 3.62.0, 3.61.1, 3.61.0, 3.60.1, 3.60.0, 3.59.1, 3.59.0, 3.58.0, 3.57.1, 3.57.0, 3.56.0, 3.55.0, 3.54.0, 3.53.1, 3.53.0, 3.52.1, 3.52.0, 3.51.1, 3.51.0, 3.50.2, 3.50.1, 3.50.0, 3.49.0, 3.48.0, 3.47.2, 3.47.1, 3.47.0, 3.46.1, 3.46.0, 3.45.0, 3.44.3, 3.44.2, 3.44.1, 3.44.0, 3.43.1, 3.43.0, 3.42.0, 3.40.2, 3.40.1, 3.40.0, 3.39.4, 3.39.3, 3.39.2, 3.39.1, 3.39.0, 3.38.0, 3.37.2, 3.37.1, 3.37.0, 3.36.0, 3.35.3, 3.35.2, 3.35.1, 3.35.0, 3.34.1, 3.34.0, 3.33.2, 3.33.1, 3.32.1, 3.32.0, 3.31.1, 3.31.0, 3.30.0, 3.29.1, 3.29.0, 3.28.0, 3.27.0, 3.26.1, 3.26.0, 3.25.1, 3.25.0, 3.24.1, 3.23.2, 3.23.0, 3.22.1, 3.22.0, 3.21.1, 3.21.0, 3.20.0, 3.19.0, 3.18.1, 3.18.0, 3.17.1, 3.17.0, 3.16.0, 3.15.0, 3.14.0, 3.13.2, 3.13.0, 3.12.0, 3.11.0, 3.10.3, 3.10.2, 3.10.1, 3.10.0, 3.9.1, 3.9.0, 3.8.0, 3.7.1, 3.7.0, 3.6.1, 3.6.0, 3.5.1, 3.4.0, 3.3.1, 3.3.0, 3.2.1, 3.2.0, 3.1.0, 3.0.0, 2.25.2, 2.25.1, 2.25.0, 2.24.1, 2.24.0, 2.23.2, 2.23.1, 2.23.0, 2.22.0, 2.21.2, 2.21.1, 2.21.0, 2.20.0, 2.19.0, 2.18.2, 2.18.1, 2.18.0, 2.17.2, 2.17.1, 2.17.0, 2.16.2, 2.16.1, 2.16.0, 2.15.6, 2.15.5, 2.15.4, 2.15.3, 2.15.2, 2.15.1, 2.15.0, 2.14.0, 2.13.2, 2.13.1, 2.13.0, 2.12.1, 2.12.0, 2.11.2, 2.11.1, 2.11.0, 2.10.2, 2.10.1, 2.10.0, 2.9.2, 2.9.1, 2.9.0, 2.8.2, 2.8.1, 2.8.0, 2.7.1, 2.7.0, 2.6.1, 2.6.0, 2.5.0, 2.4.0, 2.3.0, 2.2.1, 2.2.0, 2.1.1, 2.1.0, 2.0.0, 1.14.1, 1.14.0, 1.13.0, 1.12.1, 1.12.0, 1.11.1, 1.11.0, 1.10.1, 1.10.0, 1.9.1, 1.9.0, 1.8.1, 1.8.0, 1.7.1, 1.7.0, 1.6.1, 1.6.0, 1.5.2, 1.5.1, 1.5.0, 1.4.1, 1.4.0, 1.3.4, 1.3.3, 1.3.2, 1.3.1, 1.3.0, 1.2.0, 1.1.0, 1.0.0, 0.17.28, 0.17.27, 0.17.26, 0.17.25, 0.17.24, 0.17.23, 0.17.22, 0.17.21, 0.17.20, 0.17.19, 0.17.18, 0.17.17, 0.17.16, 0.17.15, 0.17.14, 0.17.13, 0.17.12, 0.17.11, 0.17.10, 0.17.9, 0.17.8, 0.17.7, 0.17.6, 0.17.5, 0.17.4, 0.17.3, 0.17.2, 0.17.1, 0.17.0, 0.16.19, 0.16.18, 0.16.17, 0.16.16, 0.16.15, 0.16.14, 0.16.13, 0.16.12, 0.16.11, 0.16.10, 0.16.9, 0.16.8, 0.16.7, 0.16.6, 0.16.5, 0.16.4, 0.16.3, 0.16.2, 0.16.1, 0.16.0, 0.15.4, 0.15.3, 0.15.2, 0.15.1, 0.15.0, 0.14.3, 0.14.2, 0.14.1, 0.14.0, 0.12.3, 0.12.2, 0.12.1, 0.12.0, 0.0.0\n---\n3.265.0\n","type":"text"}]}›Thinking…
Both available. Let me install pulumi CLI (via the installer script to ~/.pulumi/bin) and pulumi python SDK. Then build an example that doesn't need cloud credentials: use random and tls providers with local file:// backend. That is a real end-to-end pulumi up.
Let me install CLI.
Installing the Pulumi CLI and Python SDK.
✓$ cd /tmp && timeout 300 sh get.pulumi.com 2>&1 | tail -20; echo "EXIT=$?"✓└{"content":[{"text":"\u001b[34;1m=== Installing Pulumi 3.265.0 ===\u001b[0m\n\u001b[37;1m+ Downloading https://github.com/pulumi/pulumi/releases/download/v3.265…ctrl+o
{"content":[{"text":"\u001b[34;1m=== Installing Pulumi 3.265.0 ===\u001b[0m\n\u001b[37;1m+ Downloading https://github.com/pulumi/pulumi/releases/download/v3.265.0/pulumi-v3.265.0-linux-x64.tar.gz...\u001b[0m\n % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\ncurl: (56) Failure when receiving data from the peer\n\u001b[37;1m+ Error encountered, falling back to https://get.pulumi.com/releases/sdk/pulumi-v3.265.0-linux-x64.tar.gz...\u001b[0m\n % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\r 0 113M 0 65536 0 0 131k 0 0:14:48 --:--:-- 0:14:48 130k\r 1 113M 1 1917k 0 0 1312k 0 0:01:28 0:00:01 0:01:27 1311k\r 2 113M 2 3440k 0 0 1380k 0 0:01:24 0:00:02 0:01:22 1380k\r 4 113M 4 4730k 0 0 1362k 0 0:01:25 0:00:03 0:01:22 1362k\r 4 113M 4 5810k 0 0 1302k 0 0:01:29 0:00:04 0:01:25 1302k\r 5 113M 5 6956k 0 0 1275k 0 0:01:31 0:00:05 0:01:26 1387k\r 7 113M 7 8193k 0 0 1268k 0 0:01:31 0:00:06 0:01:25 1255k\r 8 113M 8 9560k 0 0 1280k 0 0:01:30 0:00:07 0:01:23 1230k\r 9 113M 9 10.7M 0 0 1304k 0 0:01:29 0:00:08 0:01:21 1263k\r 10 113M 10 12.3M 0 0 1336k 0 0:01:27 0:00:09 0:01:18 1367k\r 12 113M 12 14.0M 0 0 1372k 0 0:01:24 0:00:10 0:01:14 1478k\r 13 113M 13 15.2M 0 0 1364k 0 0:01:25 0:00:11 0:01:14 1487k\r 14 113M 14 16.3M 0 0 1344k 0 0:01:26 0:00:12 0:01:14 1440k\r 15 113M 15 17.5M 0 0 1332k 0 0:01:27 0:00:13 0:01:14 1380k\r 16 113M 16 18.6M 0 0 1320k 0 0:01:28 0:00:14 0:01:14 1289k\r 17 113M 17 19.7M 0 0 1304k 0 0:01:29 0:00:15 0:01:14 1163k\r 18 113M 18 20.8M 0 0 1295k 0 0:01:29 0:00:16 0:01:13 1139k\r 19 113M 19 21.8M 0 0 1279k 0 0:01:31 0:00:17 0:01:14 1117k\r 20 113M 20 22.8M 0 0 1268k 0 0:01:31 0:00:18 0:01:13 1097k\r 21 113M 21 24.0M 0 0 1264k 0 0:01:32 0:00:19 0:01:13 1101k\r 22 113M 22 25.3M 0 0 1267k 0 0:01:31 0:00:20 0:01:11 1152k\r 23 113M 23 26.7M 0 0 1275k 0 0:01:31 0:00:21 0:01:10 1210k\r 24 113M 24 28.2M 0 0 1288k 0 0:01:30 0:00:22 0:01:08 1321k\r 26 113M 26 29.5M 0 0 1289k 0 0:01:30 0:00:23 0:01:07 1367k\r 27 113M 27 30.7M 0 0 1288k 0 0:01:30 0:00:24 0:01:06 1384k\r 27 113M 27 31.7M 0 0 1275k 0 0:01:31 0:00:25 0:01:06 1309k\r 28 113M 28 32.2M 0 0 1247k 0 0:01:33 0:00:26 0:01:07 1126k\r 28 113M 28 32.8M 0 0 1223k 0 0:01:35 0:00:27 0:01:08 932k\r 29 113M 29 33.5M 0 0 1206k 0 0:01:36 0:00:28 0:01:08 810k\r 30 113M 30 34.3M 0 0 1193k 0 0:01:37 0:00:29 0:01:08 731k\r 30 113M 30 35.1M 0 0 1179k 0 0:01:38 0:00:30 0:01:08 693k\r 31 113M 31 35.7M 0 0 1162k 0 0:01:40 0:00:31 0:01:09 707k\r 31 113M 31 36.1M 0 0 1139k 0 0:01:42 0:00:32 0:01:10 680k\r 32 113M 32 36.6M 0 0 1121k 0 0:01:43 0:00:33 0:01:10 643k\r 32 113M 32 37.3M 0 0 1108k 0 0:01:45 0:00:34 0:01:11 606k\r 33 113M 33 38.0M 0 0 1099k 0 0:01:45 0:00:35 0:01:10 607k\r 34 113M 34 38.8M 0 0 1089k 0 0:01:46 0:00:36 0:01:10 632k\r 34 113M 34 39.5M 0 0 1082k 0 0:01:47 0:00:37 0:01:10 705k\r 35 113M 35 40.4M 0 0 1077k 0 0:01:48 0:00:38 0:01:10 779k\r 36 113M 36 41.2M 0 0 1070k 0 0:01:48 0:00:39 0:01:09 809k\r 36 113M 36 41.9M 0 0 1062k 0 0:01:49 0:00:40 0:01:09 800k\r 37 113M 37 42.7M 0 0 1054k 0 0:01:50 0:00:41 0:01:09 802k\r 38 113M 38 43.5M 0 0 1050k 0 0:01:50 0:00:42 0:01:08 815k\r 39 113M 39 44.5M 0 0 1048k 0 0:01:51 0:00:43 0:01:08 825k\r 39 113M 39 45.3M 0 0 1043k 0 0:01:51 0:00:44 0:01:07 831k\r 40 113M 40 46.3M 0 0 1043k 0 0:01:51 0:00:45 0:01:06 886k\r 41 113M 41 47.3M 0 0 1043k 0 0:01:51 0:00:46 0:01:05 946k\r 42 113M 42 48.2M 0 0 1040k 0 0:01:51 0:00:47 0:01:04 953k\r 43 113M 43 49.2M 0 0 1039k 0 0:01:52 0:00:48 0:01:04 963k\r 44 113M 44 50.2M 0 0 1040k 0 0:01:51 0:00:49 0:01:02 1011k\r 45 113M 45 51.4M 0 0 1043k 0 0:01:51 0:00:50 0:01:01 1047k\r 46 113M 46 52.6M 0 0 1047k 0 0:01:51 0:00:51 0:01:00 1087k\r 47 113M 47 53.8M 0 0 1050k 0 0:01:50 0:00:52 0:00:58 1145k\r 48 113M 48 54.7M 0 0 1048k 0 0:01:51 0:00:53 0:00:58 1133k\r 48 113M 48 55.5M 0 0 1043k 0 0:01:51 0:00:54 0:00:57 1074k\r 49 113M 49 56.0M 0 0 1035k 0 0:01:52 0:00:55 0:00:57 954k\r 49 113M 49 56.7M 0 0 1028k 0 0:01:53 0:00:56 0:00:57 835k\r 50 113M 50 57.4M 0 0 1024k 0 0:01:53 0:00:57 0:00:56 752k\r 51 113M 51 58.3M 0 0 1022k 0 0:01:53 0:00:58 0:00:55 749k\r 52 113M 52 59.4M 0 0 1023k 0 0:01:53 0:00:59 0:00:54 802k\r 53 113M 53 60.5M 0 0 1025k 0 0:01:53 0:01:00 0:00:53 919k\r 54 113M 54 61.8M 0 0 1030k 0 0:01:53 0:01:01 0:00:52 1050k\r 55 113M 55 63.2M 0 0 1036k 0 0:01:52 0:01:02 0:00:50 1172k\r 56 113M 56 64.6M 0 0 1043k 0 0:01:51 0:01:03 0:00:48 1290k\r 57 113M 57 65.7M 0 0 1044k 0 0:01:51 0:01:04 0:00:47 1299k\r 58 113M 58 66.8M 0 0 1046k 0 0:01:51 0:01:05 0:00:46 1293k\r 59 113M 59 68.1M 0 0 1050k 0 0:01:50 0:01:06 0:00:44 1294k\r 61 113M 61 69.5M 0 0 1055k 0 0:01:50 0:01:07 0:00:43 1294k\r 61 113M 61 70.1M 0 0 1049k 0 0:01:50 0:01:08 0:00:42 1121k\r 62 113M 62 71.1M 0 0 1048k 0 0:01:51 0:01:09 0:00:42 1093k\r 63 113M 63 72.0M 0 0 1047k 0 0:01:51 0:01:10 0:00:41 1058k\r 64 113M 64 73.1M 0 0 1048k 0 0:01:51 0:01:11 0:00:40 1018k\r 65 113M 65 74.2M 0 0 1048k 0 0:01:51 0:01:12 0:00:39 960k\r 66 113M 66 75.2M 0 0 1048k 0 0:01:51 0:01:13 0:00:38 1029k\r 66 113M 66 75.9M 0 0 1044k 0 0:01:51 0:01:14 0:00:37 990k\r 67 113M 67 76.6M 0 0 1039k 0 0:01:52 0:01:15 0:00:37 933k\r 67 113M 67 77.1M 0 0 1033k 0 0:01:52 0:01:16 0:00:36 821k\r 68 113M 68 77.5M 0 0 1024k 0 0:01:53 0:01:17 0:00:36 679k\r 68 113M 68 77.9M 0 0 1017k 0 0:01:54 0:01:18 0:00:36 561k\r 68 113M 68 78.3M 0 0 1009k 0 0:01:55 0:01:19 0:00:36 491k\r 69 113M 69 78.9M 0 0 1004k 0 0:01:55 0:01:20 0:00:35 472k\r 69 113M 69 79.4M 0 0 998k 0 0:01:56 0:01:21 0:00:35 471k\r 70 113M 70 79.9M 0 0 993k 0 0:01:57 0:01:22 0:00:35 495k\r 70 113M 70 80.4M 0 0 987k 0 0:01:57 0:01:23 0:00:34 517k\r 71 113M 71 81.1M 0 0 983k 0 0:01:58 0:01:24 0:00:34 563k\r 71 113M 71 81.6M 0 0 977k 0 0:01:59 0:01:25 0:00:34 551k\r 72 113M 72 82.0M 0 0 971k 0 0:01:59 0:01:26 0:00:33 530k\r 72 113M 72 82.5M 0 0 966k 0 0:02:00 0:01:27 0:00:33 520k\r 73 113M 73 83.0M 0 0 961k 0 0:02:01 0:01:28 0:00:33 525k\r 73 113M 73 83.6M 0 0 957k 0 0:02:01 0:01:29 0:00:32 526k\r 74 113M 74 84.4M 0 0 956k 0 0:02:01 0:01:30 0:00:31 584k\r 74 113M 74 84.9M 0 0 951k 0 0:02:02 0:01:31 0:00:31 604k\r 75 113M 75 85.3M 0 0 945k 0 0:02:03 0:01:32 0:00:31 579k\r 75 113M 75 85.8M 0 0 940k 0 0:02:03 0:01:33 0:00:30 565k\r 75 113M 75 86.4M 0 0 936k 0 0:02:04 0:01:34 0:00:30 555k\r 76 113M 76 87.1M 0 0 934k 0 0:02:04 0:01:35 0:00:29 545k\r 77 113M 77 87.8M 0 0 932k 0 0:02:04 0:01:36 0:00:28 583k\r 77 113M 77 88.5M 0 0 930k 0 0:02:05 0:01:37 0:00:28 666k\r 78 113M 78 89.3M 0 0 928k 0 0:02:05 0:01:38 0:00:27 710k\r 78 113M 78 89.8M 0 0 924k 0 0:02:05 0:01:39 0:00:26 701k\r 79 113M 79 90.4M 0 0 921k 0 0:02:06 0:01:40 0:00:26 675k\r 80 113M 80 91.1M 0 0 919k 0 0:02:06 0:01:41 0:00:25 671k\r 80 113M 80 91.7M 0 0 917k 0 0:02:06 0:01:42 0:00:24 653k\r 81 113M 81 92.4M 0 0 914k 0 0:02:07 0:01:43 0:00:24 642k\r 81 113M 81 92.7M 0 0 909k 0 0:02:08 0:01:44 0:00:24 596k\r 81 113M 81 93.1M 0 0 904k 0 0:02:08 0:01:45 0:00:23 557k\r 82 113M 82 93.7M 0 0 901k 0 0:02:09 0:01:46 0:00:23 527k\r 82 113M 82 94.3M 0 0 899k 0 0:02:09 0:01:47 0:00:22 525k\r 83 113M 83 94.7M 0 0 894k 0 0:02:10 0:01:48 0:00:22 484k\r 83 113M 83 95.1M 0 0 890k 0 0:02:10 0:01:49 0:00:21 494k\r 84 113M 84 95.6M 0 0 886k 0 0:02:11 0:01:50 0:00:21 511k\r 84 113M 84 96.2M 0 0 884k 0 0:02:11 0:01:51 0:00:20 523k\r 85 113M 85 97.0M 0 0 883k 0 0:02:11 0:01:52 0:00:19 542k\r 86 113M 86 97.8M 0 0 883k 0 0:02:11 0:01:53 0:00:18 629k\r 86 113M 86 98.8M 0 0 884k 0 0:02:11 0:01:54 0:00:17 759k\r 87 113M 87 99.9M 0 0 886k 0 0:02:11 0:01:55 0:00:16 890k\r 89 113M 89 101M 0 0 890k 0 0:02:10 0:01:56 0:00:14 1034k\r 90 113M 90 103M 0 0 899k 0 0:02:09 0:01:57 0:00:12 1256k\r 92 113M 92 104M 0 0 904k 0 0:02:08 0:01:58 0:00:10 1390k\r 93 113M 93 106M 0 0 909k 0 0:02:08 0:01:59 0:00:09 1491k\r 94 113M 94 107M 0 0 914k 0 0:02:07 0:02:00 0:00:07 1564k\r 96 113M 96 109M 0 0 921k 0 0:02:06 0:02:01 0:00:05 1627k\r 97 113M 97 111M 0 0 928k 0 0:02:05 0:02:02 0:00:03 1615k\r 99 113M 99 112M 0 0 936k 0 0:02:04 0:02:03 0:00:01 1684k\r100 113M 100 113M 0 0 940k 0 0:02:03 0:02:03 --:--:-- 1760k\n\u001b[37;1m+ Extracting to /sandbox/.pulumi/bin\u001b[0m\n\u001b[34;1m\u001b[0m\n\u001b[34;1m=== Pulumi is now installed! 🍹 ===\u001b[0m\n\u001b[37;1m+ Please add /sandbox/.pulumi/bin to your $PATH\u001b[0m\n\u001b[32;1m+ Get started with Pulumi: https://www.pulumi.com/docs/quickstart\u001b[0m\nEXIT=0\n","type":"text"}]}›Thinking…
CLI installed. Now install python SDK and set up a project. I'll avoid cloud credentials — use random and tls providers (no cloud account needed), with local file:// state backend (no Pulumi Cloud login needed).
Let me create venv and install pulumi + pulumi-random + pulumi-tls.
Note plugin downloads for random/tls providers require network to GitHub releases — that worked (slow but OK).
Let me set up.
CLI installed. Now the Python SDK and providers that need no cloud credentials (random, tls).
✓$ export PATH=/sandbox/.pulumi/bin:$PATH; pulumi version; cd /sandbox/repo && python3 -m venv .venv && . .venv/bin/activate && timeout 300 pip install -q pulumi pulumi-random pulumi-tls 2>&1 | tail -5; …✓└{"content":[{"text":"v3.265.0\nPIP_EXIT=0\npulumi 3.265.0\npulumi_random 4.21.2\npulumi_tls …ctrl+o
export PATH=/sandbox/.pulumi/bin:$PATH; pulumi version; cd /sandbox/repo && python3 -m venv .venv && . .venv/bin/activate && timeout 300 pip install -q pulumi pulumi-random pulumi-tls 2>&1 | tail -5; echo "PIP_EXIT=$?"; pip list 2>/dev/null | grep -i pulumi
{"content":[{"text":"v3.265.0\nPIP_EXIT=0\npulumi 3.265.0\npulumi_random 4.21.2\npulumi_tls 5.6.1\n","type":"text"}]}›Thinking…
Now create the project. Use local backend: pulumi login --local or PULUMI_BACKEND_URL=file://~. Also need passphrase for local backend secrets: PULUMI_CONFIG_PASSPHRASE="" (empty allowed).
Create Pulumi.yaml, main.py, requirements.txt.
Example: generate a random pet name, a random password, a self-signed TLS cert, and write outputs to a local file using a local.File? pulumi-local requires command provider. Keep it simple: random + tls.
Let's write it.
✓write /sandbox/repo/Pulumi.yaml✓└{"content":[{"text":"Successfully wrote 166 bytes to /sandbox/repo/Pulumi.yaml","type":"text"}]}
✓write /sandbox/repo/__main__.py✓└{"content":[{"text":"Successfully wrote 1307 bytes to /sandbox/repo/__main__.py","type":"text"}]}
✓write /sandbox/repo/requirements.txt✓└{"content":[{"text":"Successfully wrote 74 bytes to /sandbox/repo/requirements.txt","type":"text"}]}
›Thinking…
Now use local backend, no Pulumi Cloud login. Set PULUMI_BACKEND_URL=file://~ and PULUMI_CONFIG_PASSPHRASE="".
Run pulumi stack init + preview.
Note: pulumi preview with --non-interactive. Also need to suppress update-check.
Let me run.
Now deploying against a local file-based state backend (no Pulumi Cloud account, no login):
✓$ cd /sandbox/repo && export PATH=/sandbox/.pulumi/bin:$PATH PULUMI_BACKEND_URL=file://~ PULUMI_CONFIG_PASSPHRASE="" PULUMI_SKIP_UPDATE_CHECK=true && pulumi stack init dev 2>&1 | tail -5 && echo "=== PR…✓└{"content":[{"text":"Created stack 'dev'\n=== PREVIEW ===\nPreviewing update (dev):\n@ previewing update....\nDownloading plugin random-4.21.2: starting\nDownlo…ctrl+o
cd /sandbox/repo && export PATH=/sandbox/.pulumi/bin:$PATH PULUMI_BACKEND_URL=file://~ PULUMI_CONFIG_PASSPHRASE="" PULUMI_SKIP_UPDATE_CHECK=true && pulumi stack init dev 2>&1 | tail -5 && echo "=== PREVIEW ===" && timeout 600 pulumi preview --non-interactive --suppress-outputs 2>&1 | tail -30
{"content":[{"text":"Created stack 'dev'\n=== PREVIEW ===\nPreviewing update (dev):\n@ previewing update....\nDownloading plugin random-4.21.2: starting\nDownloading plugin tls-5.6.1: starting\n@ previewing update......................\nDownloading plugin random-4.21.2: done\nInstalling plugin random-4.21.2: starting\nInstalling plugin random-4.21.2: done\nDownloading plugin tls-5.6.1: done\nInstalling plugin tls-5.6.1: starting\n@ previewing update....\nInstalling plugin tls-5.6.1: done\n\n + pulumi:pulumi:Stack pulumi-demo-dev create \n + random:index:RandomPassword db-password create \n + random:index:RandomPet pet create \n + tls:index:PrivateKey key create \n + tls:index:SelfSignedCert cert create \n + pulumi:pulumi:Stack pulumi-demo-dev create \nResources:\n + 5 to create\n\n","type":"text"}]}›Thinking…
Preview worked. Now run pulumi up --yes.
Preview succeeded. Now the actual deploy:
✓$ cd /sandbox/repo && export PATH=/sandbox/.pulumi/bin:$PATH PULUMI_BACKEND_URL=file://~ PULUMI_CONFIG_PASSPHRASE="" PULUMI_SKIP_UPDATE_CHECK=true && timeout 600 pulumi up --yes --non-interactive 2>&1 |…✓└{"content":[{"text":" + 5 to create\n\nUpdating (dev):\n\n + pulumi:pulumi:Stack pulumi-demo-dev creating (0s) \n + random:index:RandomPet pet creating (0s…ctrl+o
cd /sandbox/repo && export PATH=/sandbox/.pulumi/bin:$PATH PULUMI_BACKEND_URL=file://~ PULUMI_CONFIG_PASSPHRASE="" PULUMI_SKIP_UPDATE_CHECK=true && timeout 600 pulumi up --yes --non-interactive 2>&1 | tail -25
{"content":[{"text":" + 5 to create\n\nUpdating (dev):\n\n + pulumi:pulumi:Stack pulumi-demo-dev creating (0s) \n + random:index:RandomPet pet creating (0s) \n + random:index:RandomPassword db-password creating (0s) \n + random:index:RandomPet pet created (0.01s) \n + tls:index:PrivateKey key creating (0s) \n + random:index:RandomPassword db-password created (0.08s) \n + tls:index:PrivateKey key created (0.06s) \n + tls:index:SelfSignedCert cert creating (0s) \n + tls:index:SelfSignedCert cert created (0.02s) \n + pulumi:pulumi:Stack pulumi-demo-dev created (0.22s) \nOutputs:\n cert_pem : \"-----BEGIN CERTIFICATE-----\\nMIIDKzCCAhOgAwIBAgIQIniuLaq3QSlMyark4/040zANBgkqhkiG9w0BAQsFADA2\\nMRQwEgYDVQQKEwtQdWx1bWkgRGVtbzEeMBwGA1UEAxMVaGFwcHktYXNwLmV4YW1w\\n...\"\n cert_subject: \"happy-asp.example.com\"\n db_password : [secret]\n name : \"happy-asp\"\n\nResources:\n + 5 created\n\nDuration: 1s\n\n","type":"text"}]}›Thinking…
Deployed successfully. Now verify idempotency (no changes on second run) and show outputs, then destroy? Actually I'll leave the stack up but demonstrate. Let me run a second preview to show "no changes" and pulumi stack output. Then optionally destroy to leave clean state — I'll show destroy works then re-up? Better: demonstrate pulumi destroy is available but leave resources. Actually keeping it deployed is fine and shows state. Let me just do the verification commands.
Also add a .gitignore entry for .venv and Pulumi.dev.yaml? Let's check gitignore.
Let me run verification and add README.
Deployed. Verifying idempotency and reading outputs:
×$ cd /sandbox/repo && export PATH=/sandbox/.pulumi/bin:$PATH PULUMI_BACKEND_URL=file://~ PULUMI_CONFIG_PASSPHRASE="" PULUMI_SKIP_UPDATE_CHECK=true && echo "=== second preview (should be no-op) ===" && t…✓└{"content":[{"text":"=== second preview (should be no-op) ===\n pulumi:pulumi:Stack pulumi-demo-dev \nResources:\n 5 unchanged\n\n=== outputs ===\nhappy-…ctrl+o
cd /sandbox/repo && export PATH=/sandbox/.pulumi/bin:$PATH PULUMI_BACKEND_URL=file://~ PULUMI_CONFIG_PASSPHRASE="" PULUMI_SKIP_UPDATE_CHECK=true && echo "=== second preview (should be no-op) ===" && timeout 300 pulumi preview --non-interactive 2>&1 | tail -4 && echo "=== outputs ===" && pulumi stack output name && pulumi stack output cert_subject && pulumi stack output --show-secrets db_password && echo "=== state location ===" && ls ~/.pulumi/stacks/
{"content":[{"text":"=== second preview (should be no-op) ===\n pulumi:pulumi:Stack pulumi-demo-dev \nResources:\n 5 unchanged\n\n=== outputs ===\nhappy-asp\nhappy-asp.example.com\nqngJLw$b13OOnjTDHpA0V5u6\n=== state location ===\nls: cannot access '/sandbox/.pulumi/stacks/': No such file or directory\n\n\nCommand exited with code 2","type":"text"}],"details":{}}✓$ cd /sandbox/repo && cat .gitignore✓└{"content":[{"text":"# Keep generated and vendored files out of source diffs.\n\n# Logs / OS cruft\n*.log\n.DS_Store\n\n# JavaScript / TypeScript\nnode_modules/…ctrl+o
{"content":[{"text":"# Keep generated and vendored files out of source diffs.\n\n# Logs / OS cruft\n*.log\n.DS_Store\n\n# JavaScript / TypeScript\nnode_modules/\n.next/\ndist/\nbuild/\ncoverage/\n\n# Python\n.venv/\nvenv/\n__pycache__/\n*.py[cod]\n.pytest_cache/\n*.egg-info/\n\n# Rust\ntarget/\n\n# Go / PHP / Ruby vendored deps\nvendor/\n.bundle/\n\n# Java / Kotlin / Scala\n.gradle/\n\n# .NET\nbin/\nobj/\n\n# Elixir\n_build/\ndeps/\n\n# Dart / Flutter\n.dart_tool/\n","type":"text"}]}›Thinking…
Everything works. The state location is elsewhere — file://~ resolved to ~/.pulumi/stacks but home may be different. Let me find it.
Also add Pulumi.dev.yaml to gitignore? Actually Pulumi.<stack>.yaml is normally committed (config, no secrets unless encrypted secrets which ARE safe to commit). But since we used a local passphrase-based secrets provider, the encrypted secrets are only decryptable with that passphrase. I'll note it. Let me find the state dir.
✓$ cd /sandbox/repo && echo "HOME=$HOME" && find / -name "*.json" -path "*stacks*" 2>/dev/null | head; ls -la ~/.pulumi 2>/dev/null | head; echo "--- config file ---"; cat Pulumi.dev.yaml 2>/dev/null || …✓└{"content":[{"text":"HOME=/sandbox\n/root/.pulumi/stacks/pulumi-demo/dev.json\ntotal 12\ndrwx------ 3 root root 4096 Sep 29 08:20 .\ndrwxr-xr-x 9 root root 4096…ctrl+o
cd /sandbox/repo && echo "HOME=$HOME" && find / -name "*.json" -path "*stacks*" 2>/dev/null | head; ls -la ~/.pulumi 2>/dev/null | head; echo "--- config file ---"; cat Pulumi.dev.yaml 2>/dev/null || echo "(none)"
{"content":[{"text":"HOME=/sandbox\n/root/.pulumi/stacks/pulumi-demo/dev.json\ntotal 12\ndrwx------ 3 root root 4096 Sep 29 08:20 .\ndrwxr-xr-x 9 root root 4096 Sep 29 08:20 ..\ndrwx------ 2 root root 4096 Sep 29 08:20 bin\n--- config file ---\nencryptionsalt: v1:n/dNJ+asPGM=:v1:HtgcbdG4NAI1vA63:1cWJAfRkRnHluuK9ILouZqZlwghCUg==\n","type":"text"}]}›Thinking…
State is at /root/.pulumi/stacks/pulumi-demo/dev.json (because the pulumi process runs as root and ~ expanded to /root).
Now let me write a README and a Makefile-ish helper, plus a script for env. Also add Pulumi.dev.yaml note.
Let me add a small README documenting pricing, and add to .gitignore the local state? State is outside repo, fine.
Let me write README.md.
Pricing info: Pulumi pricing model. Let me verify current pricing from their site rather than rely on memory. Let me fetch pulumi.com/pricing.
Everything works end-to-end. Let me verify current pricing from the source rather than guess:
✓$ cd /tmp && timeout 40 curl -fsSL https://www.pulumi.com/pricing/ -o pricing.html 2>&1 && python3 - <<'EOF'✓└{"content":[{"text":"rkshops Community Join 10k+ developers on Slack Engineers love Pulumi Hear from engineers why they love us Releases Major platform updates …ctrl+o
cd /tmp && timeout 40 curl -fsSL https://www.pulumi.com/pricing/ -o pricing.html 2>&1 && python3 - <<'EOF'
import re,html
t=open('/tmp/pricing.html',encoding='utf-8',errors='ignore').read()
t=re.sub(r'<script.*?</script>','',t,flags=re.S)
t=re.sub(r'<style.*?</style>','',t,flags=re.S)
t=re.sub(r'<[^>]+>',' ',t)
t=html.unescape(re.sub(r'\s+',' ',t))
i=t.lower().find('free')
print(t[max(0,i-2500):i+3000])
EOF
{"content":[{"text":"rkshops Community Join 10k+ developers on Slack Engineers love Pulumi Hear from engineers why they love us Releases Major platform updates from the team Enterprise solutions Security, compliance, and support for teams Agentic infrastructure The path to agents operating your infrastructure at scale Customers Get a glimpse of who's using Pulumi and hear their stories Request a demo See how Pulumi can help your team Professional services Get expert help with your implementation Contact sales Talk to our team about your needs About us Our purpose and values Careers Come work for Pulumi Newsroom Pulumi in the news Awards Recognition from press and analysts 25.7K Contact us Sign in Dashboard Get started 25.7K Contact us Sign in Dashboard Get started Navigation Product Core product Platform overview Everything platform engineering teams need to build, secure, and scale cloud infrastructure Infrastructure as code IaC for any cloud, in any language — Node.js, Python, Go, .NET, Java, and YAML Key capabilities AI infrastructure agent Meet Neo, our AI-powered infrastructure engineering agent Secrets & configuration Environments, secrets, and configuration management Discovery & governance Asset management, compliance remediation, and AI insights over the cloud Internal developer platform The fastest, most secure way to deliver cloud infrastructure For engineers Get started Install Pulumi and deploy your first project in minutes Documentation Complete guides and API references Registry Browse hundreds of cloud providers and packages Dev Center Tutorials, templates, examples, and more Events and workshops Live sessions and workshops Community Join 10k+ developers on Slack Engineers love Pulumi Hear from engineers why they love us Releases Major platform updates from the team For enterprises Enterprise solutions Security, compliance, and support for teams Agentic infrastructure The path to agents operating your infrastructure at scale Customers Get a glimpse of who's using Pulumi and hear their stories Request a demo See how Pulumi can help your team Professional services Get expert help with your implementation Contact sales Talk to our team about your needs Docs Registry Blog Pricing Company About us Our purpose and values Careers Come work for Pulumi Newsroom Pulumi in the news Awards Recognition from press and analysts 25.7K Contact us Sign in Dashboard Get started Copy logo SVG Download mark Get other variants Read brand guide Plans for teams of all sizes Start for free. Stay free forever for open source and individuals, or scale with advanced editions for your team. Free Get started with Pulumi-managed infrastructure at no cost. $0 No credit card required Get started Includes: 1 user IaC state management Pulumi Deployments Basic Pulumi ESC Unlimited projects, stacks, and environments Unlimited updates and history Up to 500 workflow minutes View all features Essentials Managed infrastructure fundamentals for your whole team. $40 /month Includes 40 Credits Up to 500 managed resources. Credits cover usage across Pulumi services. Additional usage is billed on demand (1 Credit = $1). 1 IaC resource 0.1825/month 1 managed secret 0.50/month 1 discovered resource 0.0185/month 1 workflow minute 0.01 1M Neo tokens 3.00 Start with Essentials Everything in Free , plus: Unlimited users Secure collaboration and CI/CD Neo code reviews and Slack integration Resource Search and Property Search OIDC and org access tokens Webhooks Automatic secrets rotation Policy results in advisory mode Service-level objectives, an uptime commitment, and support tickets View all features Estimate your cost Pro Popular Automation and built-in governance for growing organizations. $400 /month Includes 400 Credits Up to 2,000 managed resources. Credits cover usage across Pulumi services. Additional usage is billed on demand (1 Credit = $1). 1 IaC resource from 0.365/month 1 managed secret 0.75/month 1 discovered resource from 0.0365/month 1 workflow minute 0.01 1M Neo tokens 3.00 Start with Pro Contact sales Everything in Essentials , plus: SAML/SSO and advanced RBAC Organization-managed policy enforcement Preventative policies Custom policy packs Neo scheduled tasks Internal developer platform (IDP) Automated audit log export Drift detection and remediation Time-to-live stacks Customer-managed keys View all features Estimate your cost Enterprise Compliance, control, and governance for at-scale operations. $2,000 /month Includes 2,000 Credits Up to 4,750 managed resources. Credits cover usage across Pulumi services. Additional usage is billed on demand (1 Credit = $1). 1 IaC resource from 0.5475/month 1 managed secret 1.00/month 1 discovered resource 0.05475/month 1 workflow minute 0.01 1M Neo tokens 3.00 Start with Enterprise Contact sales Everything in Pro , plus: Self-hosting available Conformance packs for CIS, CIS Kubernetes, CMMC, HITRUST, ISO 27001, NIST, and PCI DSS Automatic group & user sync (SCIM) Unlimited custom policy packs Policy remediation Contract pricing and invoicing View all features Estimate your cost Are you a scaling startup? Apply now to receive $10,000 in free Credits. strong]:text-violet-primary\"> 5x faster time to market strong]:text-violet-primary\"> 100 days saved each year with Pulumi Cloud instead of DIY state management strong]:text-violet-primary\"> 10x faster deployments , from weeks to hours Free $0 Get started Essentials $40 /month Start with Essentials Pro $400 /month Start with Pro Enterprise $2,000 /month\n","type":"text"}]}›Thinking…
Got real pricing. Now write README documenting everything. Then optionally destroy the stack to leave a clean repo? I'll leave it deployed but document pulumi destroy. Actually, better hygiene: I'll leave it and mention. Hmm — I created real state. It costs nothing (local backend, no cloud resources). I'll leave it and note how to destroy.
Actually, let me demonstrate destroy to prove the full lifecycle and leave the repo clean, then re-up? That's extra time. I'll just show pulumi destroy in docs and leave the stack. Let me add a Makefile for convenience and README.
Let me add README.md and a small env script.
Now documentation and a convenience wrapper:
✓write /sandbox/repo/README.md✓└{"content":[{"text":"Successfully wrote 3187 bytes to /sandbox/repo/README.md","type":"text"}]}
✓write /sandbox/repo/pulumi.sh✓└{"content":[{"text":"Successfully wrote 495 bytes to /sandbox/repo/pulumi.sh","type":"text"}]}
›Thinking…
Note: the wrapper sets HOME-based path but pulumi installed to /sandbox/.pulumi/bin while state went to /root/.pulumi. That's because HOME=/sandbox in my shell but the process ran as root with ~ expansion differently? Actually file://~ — pulumi expands ~ itself. HOME=/sandbox per echo. But state landed in /root/.pulumi/stacks. Odd — likely pulumi uses os/user home dir (from /etc/passwd for root) = /root. So ~ in the backend URL resolves to /root.
I should make the wrapper explicit to avoid ambiguity. Let me fix: use file:///root/.pulumi/stacks? That's non-portable. Better: file://$(pwd)/.pulumi-state — state in repo dir, gitignored. That's clearer and portable. But then I'd need to re-init the stack. Let me do that: it's cleaner and self-contained.
Let me update wrapper to use a repo-local state dir, add to .gitignore, and re-run to verify.
Also chmod +x.
Let me verify the wrapper works and re-create stack in new location.
Fixing one ambiguity: file://~ resolved to /root (not $HOME=/sandbox). Making state repo-local and explicit instead:
✓edit /sandbox/repo/pulumi.sh✓└{"content":[{"text":"Successfully replaced 1 block(s) in /sandbox/repo/pulumi.sh.","type":"text"}],"details":{"diff":" ...\n 6 set -euo pipefail\n 7 cd \"$…ctrl+o
{"content":[{"text":"Successfully replaced 1 block(s) in /sandbox/repo/pulumi.sh.","type":"text"}],"details":{"diff":" ...\n 6 set -euo pipefail\n 7 cd \"$(dirname \"$0\")\"\n 8 \n 9 export PATH=\"${HOME}/.pulumi/bin:${PATH}\"\n-10 export PULUMI_BACKEND_URL=\"${PULUMI_BACKEND_URL:-file://~}\"\n+10 # Repo-local state dir. Note: `file://~` is expanded by the CLI using the OS\n+11 # account's home (/root here), not $HOME (/sandbox) -- so pin it explicitly.\n+12 export PULUMI_BACKEND_URL=\"${PULUMI_BACKEND_URL:-file://$(pwd)/.pulumi-state}\"\n 11 export PULUMI_CONFIG_PASSPHRASE=\"${PULUMI_CONFIG_PASSPHRASE:-}\"\n 12 export PULUMI_SKIP_UPDATE_CHECK=true\n 13 \n 14 exec pulumi --non-interactive \"$@\"","patch":"--- /sandbox/repo/pulumi.sh\n+++ /sandbox/repo/pulumi.sh\n@@ -6,9 +6,11 @@\n set -euo pipefail\n cd \"$(dirname \"$0\")\"\n \n export PATH=\"${HOME}/.pulumi/bin:${PATH}\"\n-export PULUMI_BACKEND_URL=\"${PULUMI_BACKEND_URL:-file://~}\"\n+# Repo-local state dir. Note: `file://~` is expanded by the CLI using the OS\n+# account's home (/root here), not $HOME (/sandbox) -- so pin it explicitly.\n+export PULUMI_BACKEND_URL=\"${PULUMI_BACKEND_URL:-file://$(pwd)/.pulumi-state}\"\n export PULUMI_CONFIG_PASSPHRASE=\"${PULUMI_CONFIG_PASSPHRASE:-}\"\n export PULUMI_SKIP_UPDATE_CHECK=true\n \n exec pulumi --non-interactive \"$@\"\n","firstChangedLine":10}}✓edit /sandbox/repo/.gitignore✓└{"content":[{"text":"Successfully replaced 1 block(s) in /sandbox/repo/.gitignore.","type":"text"}],"details":{"diff":" ...\n 10 dist/\n 11 build/\n 12 cover…ctrl+o
{"content":[{"text":"Successfully replaced 1 block(s) in /sandbox/repo/.gitignore.","type":"text"}],"details":{"diff":" ...\n 10 dist/\n 11 build/\n 12 coverage/\n 13 \n+14 # Pulumi local (file-based) state backend\n+15 .pulumi-state/\n+16 \n 14 # Python\n 15 .venv/\n 16 venv/\n 17 __pycache__/\n ...","patch":"--- /sandbox/repo/.gitignore\n+++ /sandbox/repo/.gitignore\n@@ -10,8 +10,11 @@\n dist/\n build/\n coverage/\n \n+# Pulumi local (file-based) state backend\n+.pulumi-state/\n+\n # Python\n .venv/\n venv/\n __pycache__/\n","firstChangedLine":14}}›Thinking…
Now re-run with the new backend. Old stack lives in /root/.pulumi/stacks. With new backend URL, pulumi stack init dev will create fresh. But Pulumi.dev.yaml already exists — fine, stack config file persists.
Let me chmod, destroy old stack (cleanup), and re-up on new backend to verify the wrapper end to end.
Also update README to reflect the new backend path.
✓$ cd /sandbox/repo && chmod +x pulumi.sh && export PATH=/sandbox/.pulumi/bin:$PATH PULUMI_BACKEND_URL=file://~ PULUMI_CONFIG_PASSPHRASE="" && echo "=== cleanup old stack ===" && timeout 300 pulumi destr…✓└{"content":[{"text":"=== cleanup old stack ===\nDuration: 1s\n\nThe resources in the stack have been deleted, but the history and configuration associated with …ctrl+o
cd /sandbox/repo && chmod +x pulumi.sh && export PATH=/sandbox/.pulumi/bin:$PATH PULUMI_BACKEND_URL=file://~ PULUMI_CONFIG_PASSPHRASE="" && echo "=== cleanup old stack ===" && timeout 300 pulumi destroy --yes --non-interactive 2>&1 | tail -4 && pulumi stack rm --yes --force dev 2>&1 | tail -2
{"content":[{"text":"=== cleanup old stack ===\nDuration: 1s\n\nThe resources in the stack have been deleted, but the history and configuration associated with the stack are still maintained. \nIf you want to remove the stack completely, run `pulumi stack rm dev`.\nStack 'dev' has been removed!\n","type":"text"}]}›Thinking…
Now re-run full lifecycle via wrapper on the new backend.
Full lifecycle re-run through the wrapper, on repo-local state:
×$ cd /sandbox/repo && ./pulumi.sh stack init dev 2>&1 | tail -3 && echo "=== UP ===" && timeout 600 ./pulumi.sh up --yes 2>&1 | tail -12 && echo "=== OUTPUTS ===" && ./pulumi.sh stack output name && ./p…✓└{"content":[{"text":"error: unable to open state directory \"file:///sandbox/repo/.pulumi-state\": stat /sandbox/repo/.pulumi-state: no such file or directory\n…ctrl+o
cd /sandbox/repo && ./pulumi.sh stack init dev 2>&1 | tail -3 && echo "=== UP ===" && timeout 600 ./pulumi.sh up --yes 2>&1 | tail -12 && echo "=== OUTPUTS ===" && ./pulumi.sh stack output name && ./pulumi.sh stack output --show-secrets db_password && echo "=== IDEMPOTENT PREVIEW ===" && timeout 300 ./pulumi.sh preview 2>&1 | tail -3 && echo "=== STATE ===" && find .pulumi-state -type f
{"content":[{"text":"error: unable to open state directory \"file:///sandbox/repo/.pulumi-state\": stat /sandbox/repo/.pulumi-state: no such file or directory\n=== UP ===\nerror: unable to open state directory \"file:///sandbox/repo/.pulumi-state\": stat /sandbox/repo/.pulumi-state: no such file or directory\n=== OUTPUTS ===\nerror: unable to open state directory \"file:///sandbox/repo/.pulumi-state\": stat /sandbox/repo/.pulumi-state: no such file or directory\n\n\nCommand exited with code 1","type":"text"}],"details":{}}›Thinking…
Need to create the dir. Add mkdir -p in wrapper.
Needs the directory to pre-exist — fixing the wrapper:
✓edit /sandbox/repo/pulumi.sh✓└{"content":[{"text":"Successfully replaced 1 block(s) in /sandbox/repo/pulumi.sh.","type":"text"}],"details":{"diff":" ...\n 9 export PATH=\"${HOME}/.pulumi…ctrl+o
{"content":[{"text":"Successfully replaced 1 block(s) in /sandbox/repo/pulumi.sh.","type":"text"}],"details":{"diff":" ...\n 9 export PATH=\"${HOME}/.pulumi/bin:${PATH}\"\n 10 # Repo-local state dir. Note: `file://~` is expanded by the CLI using the OS\n 11 # account's home (/root here), not $HOME (/sandbox) -- so pin it explicitly.\n 12 export PULUMI_BACKEND_URL=\"${PULUMI_BACKEND_URL:-file://$(pwd)/.pulumi-state}\"\n+13 # The CLI will not create a missing file-backend root, so make it first.\n+14 mkdir -p \"${PULUMI_BACKEND_URL#file://}\"\n 13 export PULUMI_CONFIG_PASSPHRASE=\"${PULUMI_CONFIG_PASSPHRASE:-}\"\n 14 export PULUMI_SKIP_UPDATE_CHECK=true\n 15 \n 16 exec pulumi --non-interactive \"$@\"","patch":"--- /sandbox/repo/pulumi.sh\n+++ /sandbox/repo/pulumi.sh\n@@ -9,8 +9,10 @@\n export PATH=\"${HOME}/.pulumi/bin:${PATH}\"\n # Repo-local state dir. Note: `file://~` is expanded by the CLI using the OS\n # account's home (/root here), not $HOME (/sandbox) -- so pin it explicitly.\n export PULUMI_BACKEND_URL=\"${PULUMI_BACKEND_URL:-file://$(pwd)/.pulumi-state}\"\n+# The CLI will not create a missing file-backend root, so make it first.\n+mkdir -p \"${PULUMI_BACKEND_URL#file://}\"\n export PULUMI_CONFIG_PASSPHRASE=\"${PULUMI_CONFIG_PASSPHRASE:-}\"\n export PULUMI_SKIP_UPDATE_CHECK=true\n \n exec pulumi --non-interactive \"$@\"\n","firstChangedLine":13}}›Thinking…
Wait: if PULUMI_BACKEND_URL is a non-file URL (e.g. pulumi.com), the mkdir would create a bogus dir. Let me guard it to only mkdir for file:// URLs.
✓edit /sandbox/repo/pulumi.sh✓└{"content":[{"text":"Successfully replaced 1 block(s) in /sandbox/repo/pulumi.sh.","type":"text"}],"details":{"diff":" ...\n 10 # Repo-local state dir. Note:…ctrl+o
{"content":[{"text":"Successfully replaced 1 block(s) in /sandbox/repo/pulumi.sh.","type":"text"}],"details":{"diff":" ...\n 10 # Repo-local state dir. Note: `file://~` is expanded by the CLI using the OS\n 11 # account's home (/root here), not $HOME (/sandbox) -- so pin it explicitly.\n 12 export PULUMI_BACKEND_URL=\"${PULUMI_BACKEND_URL:-file://$(pwd)/.pulumi-state}\"\n 13 # The CLI will not create a missing file-backend root, so make it first.\n-14 mkdir -p \"${PULUMI_BACKEND_URL#file://}\"\n+14 case \"$PULUMI_BACKEND_URL\" in\n+15 file://*) mkdir -p \"${PULUMI_BACKEND_URL#file://}\" ;;\n+16 esac\n 15 export PULUMI_CONFIG_PASSPHRASE=\"${PULUMI_CONFIG_PASSPHRASE:-}\"\n 16 export PULUMI_SKIP_UPDATE_CHECK=true\n 17 \n 18 exec pulumi --non-interactive \"$@\"","patch":"--- /sandbox/repo/pulumi.sh\n+++ /sandbox/repo/pulumi.sh\n@@ -10,9 +10,11 @@\n # Repo-local state dir. Note: `file://~` is expanded by the CLI using the OS\n # account's home (/root here), not $HOME (/sandbox) -- so pin it explicitly.\n export PULUMI_BACKEND_URL=\"${PULUMI_BACKEND_URL:-file://$(pwd)/.pulumi-state}\"\n # The CLI will not create a missing file-backend root, so make it first.\n-mkdir -p \"${PULUMI_BACKEND_URL#file://}\"\n+case \"$PULUMI_BACKEND_URL\" in\n+ file://*) mkdir -p \"${PULUMI_BACKEND_URL#file://}\" ;;\n+esac\n export PULUMI_CONFIG_PASSPHRASE=\"${PULUMI_CONFIG_PASSPHRASE:-}\"\n export PULUMI_SKIP_UPDATE_CHECK=true\n \n exec pulumi --non-interactive \"$@\"\n","firstChangedLine":14}}›Thinking…
Now re-run.
✓$ cd /sandbox/repo && ./pulumi.sh stack init dev 2>&1 | tail -3 && echo "=== UP ===" && timeout 600 ./pulumi.sh up --yes 2>&1 | tail -12 && echo "=== OUTPUTS ===" && ./pulumi.sh stack output name && ./p…✓└{"content":[{"text":"Created stack 'dev'\n=== UP ===\n + pulumi:pulumi:Stack pulumi-demo-dev created (0.26s) \nOutputs:\n cert_pem : \"-----BEGIN CERTIFI…ctrl+o
cd /sandbox/repo && ./pulumi.sh stack init dev 2>&1 | tail -3 && echo "=== UP ===" && timeout 600 ./pulumi.sh up --yes 2>&1 | tail -12 && echo "=== OUTPUTS ===" && ./pulumi.sh stack output name && ./pulumi.sh stack output --show-secrets db_password && echo "=== IDEMPOTENT PREVIEW ===" && timeout 300 ./pulumi.sh preview 2>&1 | tail -3 && echo "=== STATE FILES ===" && find .pulumi-state -type f | head
{"content":[{"text":"Created stack 'dev'\n=== UP ===\n + pulumi:pulumi:Stack pulumi-demo-dev created (0.26s) \nOutputs:\n cert_pem : \"-----BEGIN CERTIFICATE-----\\nMIIDLzCCAhegAwIBAgIQblxPbTkKnaWCKL2sDOXs1DANBgkqhkiG9w0BAQsFADA4\\nMRQwEgYDVQQKEwtQdWx1bWkgRGVtbzEgMB4GA1UEAxMXZmFpci10dXJ0bGUuZXhh\\n...\"\n cert_subject: \"fair-turtle.example.com\"\n db_password : [secret]\n name : \"fair-turtle\"\n\nResources:\n + 5 created\n\nDuration: 1s\n\n=== OUTPUTS ===\nfair-turtle\nC9ttHeViyYmaHFmGucy0RvNw\n=== IDEMPOTENT PREVIEW ===\nResources:\n 5 unchanged\n\n=== STATE FILES ===\n.pulumi-state/.pulumi/meta.yaml\n.pulumi-state/.pulumi/history/pulumi-demo/dev/dev-1790670207884996960.history.json.attrs\n.pulumi-state/.pulumi/history/pulumi-demo/dev/dev-1790670207884996960.checkpoint.json\n.pulumi-state/.pulumi/history/pulumi-demo/dev/dev-1790670207884996960.history.json\n.pulumi-state/.pulumi/history/pulumi-demo/dev/dev-1790670207884996960.checkpoint.json.attrs\n.pulumi-state/.pulumi/meta.yaml.attrs\n.pulumi-state/.pulumi/stacks/pulumi-demo/dev.json.bak.attrs\n.pulumi-state/.pulumi/stacks/pulumi-demo/dev.json\n.pulumi-state/.pulumi/stacks/pulumi-demo/dev.json.bak\n.pulumi-state/.pulumi/stacks/pulumi-demo/dev.json.attrs\n","type":"text"}]}›Thinking…
Everything works. Update README to reflect the repo-local state path and the mkdir gotcha. Then final git status / summary.
Let me update README sections.
Updating the README to match the final setup:
✓edit /sandbox/repo/README.md✓└{"content":[{"text":"Successfully replaced 4 block(s) in /sandbox/repo/README.md.","type":"text"}],"details":{"diff":" ...\n 4 credentials**. It uses the `r…ctrl+o
{"content":[{"text":"Successfully replaced 4 block(s) in /sandbox/repo/README.md.","type":"text"}],"details":{"diff":" ...\n 4 credentials**. It uses the `random` and `tls` providers, which generate everything\n 5 locally, so the full IaC lifecycle (`preview` -> `up` -> `destroy`) is exercisable\n 6 without touching AWS/Azure/GCP.\n 7 \n- 8 State is kept in a **local file-based backend** (`file://~`), so no Pulumi Cloud\n- 9 login or account is needed either.\n+ 8 State is kept in a **local file-based backend** (`file://./.pulumi-state`), so no\n+ 9 Pulumi Cloud login or account is needed either.\n 10 \n 11 ## What it creates\n 12 \n 13 | Resource | Provider | Purpose |\n ...\n 26 Pulumi.yaml project + runtime declaration (python, uses ./.venv)\n 27 __main__.py the program — all resources and outputs\n 28 requirements.txt pinned SDK + provider packages\n 29 pulumi.sh wrapper that sets PATH + local-backend env vars\n+30 .pulumi-state/ local state backend (gitignored)\n 30 ```\n 31 \n 32 ## Usage\n 33 \n ...\n 43 ```\n 44 \n 45 `pulumi.sh` exports:\n 46 \n-47 - `PULUMI_BACKEND_URL=file://~` — state as local JSON under `~/.pulumi/stacks/`\n+48 - `PULUMI_BACKEND_URL=file://./.pulumi-state` — state as local JSON, and\n+49 `mkdir -p`s the directory (the CLI refuses to create a missing backend root)\n 48 - `PULUMI_CONFIG_PASSPHRASE=\"\"` — encrypts secrets with an empty passphrase\n 49 (local dev only; use a real passphrase or KMS in production)\n 50 - `PULUMI_SKIP_UPDATE_CHECK=true`\n 51 \n+54 Avoid `file://~`: the CLI expands `~` from the OS account's home (`/root` for\n+55 root), **not** `$HOME`, which silently puts state somewhere unexpected.\n+56 \n 52 ## Verified run\n 53 \n 54 ```\n 55 Updating (dev):\n-56 + pulumi:pulumi:Stack pulumi-demo-dev created (0.22s)\n-57 + random:index:RandomPet pet created (0.01s)\n-58 + random:index:RandomPassword db-password created (0.08s)\n-59 + tls:index:PrivateKey key created (0.06s)\n-60 + tls:index:SelfSignedCert cert created (0.02s)\n+61 + pulumi:pulumi:Stack pulumi-demo-dev created (0.26s)\n 61 Outputs:\n-62 cert_subject: \"happy-asp.example.com\"\n+63 cert_subject: \"fair-turtle.example.com\"\n 63 db_password : [secret]\n-64 name : \"happy-asp\"\n+65 name : \"fair-turtle\"\n 65 Resources: + 5 created\n+67 Duration: 1s\n 66 ```\n 67 \n 68 A second `pulumi preview` reports `5 unchanged`, confirming the program is\n 69 idempotent.\n ...","patch":"--- /sandbox/repo/README.md\n+++ /sandbox/repo/README.md\n@@ -4,10 +4,10 @@\n credentials**. It uses the `random` and `tls` providers, which generate everything\n locally, so the full IaC lifecycle (`preview` -> `up` -> `destroy`) is exercisable\n without touching AWS/Azure/GCP.\n \n-State is kept in a **local file-based backend** (`file://~`), so no Pulumi Cloud\n-login or account is needed either.\n+State is kept in a **local file-based backend** (`file://./.pulumi-state`), so no\n+Pulumi Cloud login or account is needed either.\n \n ## What it creates\n \n | Resource | Provider | Purpose |\n@@ -26,8 +26,9 @@\n Pulumi.yaml project + runtime declaration (python, uses ./.venv)\n __main__.py the program — all resources and outputs\n requirements.txt pinned SDK + provider packages\n pulumi.sh wrapper that sets PATH + local-backend env vars\n+.pulumi-state/ local state backend (gitignored)\n ```\n \n ## Usage\n \n@@ -43,27 +44,28 @@\n ```\n \n `pulumi.sh` exports:\n \n-- `PULUMI_BACKEND_URL=file://~` — state as local JSON under `~/.pulumi/stacks/`\n+- `PULUMI_BACKEND_URL=file://./.pulumi-state` — state as local JSON, and\n+ `mkdir -p`s the directory (the CLI refuses to create a missing backend root)\n - `PULUMI_CONFIG_PASSPHRASE=\"\"` — encrypts secrets with an empty passphrase\n (local dev only; use a real passphrase or KMS in production)\n - `PULUMI_SKIP_UPDATE_CHECK=true`\n \n+Avoid `file://~`: the CLI expands `~` from the OS account's home (`/root` for\n+root), **not** `$HOME`, which silently puts state somewhere unexpected.\n+\n ## Verified run\n \n ```\n Updating (dev):\n- + pulumi:pulumi:Stack pulumi-demo-dev created (0.22s)\n- + random:index:RandomPet pet created (0.01s)\n- + random:index:RandomPassword db-password created (0.08s)\n- + tls:index:PrivateKey key created (0.06s)\n- + tls:index:SelfSignedCert cert created (0.02s)\n+ + pulumi:pulumi:Stack pulumi-demo-dev created (0.26s)\n Outputs:\n- cert_subject: \"happy-asp.example.com\"\n+ cert_subject: \"fair-turtle.example.com\"\n db_password : [secret]\n- name : \"happy-asp\"\n+ name : \"fair-turtle\"\n Resources: + 5 created\n+Duration: 1s\n ```\n \n A second `pulumi preview` reports `5 unchanged`, confirming the program is\n idempotent.\n","firstChangedLine":8}}›Thinking…
Now final check: git status, cat pulumi.sh. Then write the summary including pricing and manageability assessment.
Let me do final verification.
✓$ cd /sandbox/repo && cat pulumi.sh && echo "=== git status ===" && git status --short && echo "=== stack ls ===" && ./pulumi.sh stack ls 2>&1 | tail -3✓└{"content":[{"text":"#!/usr/bin/env bash\n# Wrapper that runs the Pulumi CLI against a local, file-based state backend.\n# No Pulumi Cloud account and no cloud …ctrl+o
{"content":[{"text":"#!/usr/bin/env bash\n# Wrapper that runs the Pulumi CLI against a local, file-based state backend.\n# No Pulumi Cloud account and no cloud credentials required.\n#\n# Usage: ./pulumi.sh <args> e.g. ./pulumi.sh preview\nset -euo pipefail\ncd \"$(dirname \"$0\")\"\n\nexport PATH=\"${HOME}/.pulumi/bin:${PATH}\"\n# Repo-local state dir. Note: `file://~` is expanded by the CLI using the OS\n# account's home (/root here), not $HOME (/sandbox) -- so pin it explicitly.\nexport PULUMI_BACKEND_URL=\"${PULUMI_BACKEND_URL:-file://$(pwd)/.pulumi-state}\"\n# The CLI will not create a missing file-backend root, so make it first.\ncase \"$PULUMI_BACKEND_URL\" in\n file://*) mkdir -p \"${PULUMI_BACKEND_URL#file://}\" ;;\nesac\nexport PULUMI_CONFIG_PASSPHRASE=\"${PULUMI_CONFIG_PASSPHRASE:-}\"\nexport PULUMI_SKIP_UPDATE_CHECK=true\n\nexec pulumi --non-interactive \"$@\"\n=== git status ===\n M .gitignore\n?? Pulumi.dev.yaml\n?? Pulumi.yaml\n?? README.md\n?? __main__.py\n?? pulumi.sh\n?? requirements.txt\n=== stack ls ===\nNAME LAST UPDATE RESOURCE COUNT\ndev* 15 seconds ago 7\n","type":"text"}]}›Thinking…
Everything done. Now write the summary: what I built, how pricing works, and manageability assessment. Note the "resource count 7" vs 5 created — includes parent/stack + provider resources. Fine.
Now the answer. Include pricing (real, from site), and manageability verdict.
Be concise but complete.
Done — built and verified end-to-end. Nothing blocked me.
What I built (/sandbox/repo)
Pulumi does have a developer workflow I can act on (CLI + Python SDK), so I proceeded. I deliberately chose the random and tls providers — they generate everything locally, so the full IaC lifecycle is exercisable with no cloud account, no credentials, and no Pulumi Cloud login. State goes to a local file backend.
| File | Role |
|---|---|
Pulumi.yaml | project + Python runtime (uses ./.venv) |
__main__.py | RandomPet, RandomPassword (secret), PrivateKey, SelfSignedCert + outputs |
requirements.txt | pulumi, pulumi-random, pulumi-tls |
pulumi.sh | wrapper setting PATH + local-backend env |
README.md | usage, gotchas, verified output |
Verified working, not just written:
+ pulumi:pulumi:Stack pulumi-demo-dev created (0.26s)
Outputs:
cert_subject: "fair-turtle.example.com"
db_password : [secret] # masked by default
name : "fair-turtle"
Resources: + 5 created Duration: 1s
Second pulumi preview → 5 unchanged (idempotent). destroy and stack rm also verified.
Three real gotchas I hit and fixed, all noted in the README:
file://~is expanded from the OS account home (/root), not$HOME(/sandbox) — state silently lands elsewhere. Pinned to an explicit repo-local path.- The CLI refuses to create a missing file-backend root; the wrapper
mkdir -ps it (guarded tofile://only). - First run downloads provider plugins from GitHub releases; the primary
github.comURL failed withcurl (56)and the installer's ownget.pulumi.com/releasesfallback saved it.
How pricing works
Pulumi is open-source and free forever as a CLI — that's what I used, cost $0. You pay only for Pulumi Cloud (state management, policy, CI/CD), per tier (verified from pulumi.com/pricing):
| Tier | Price | Included | Managed resources |
|---|---|---|---|
| Free | $0 | 1 user, state, Deployments, basic ESC, unlimited projects/updates, 500 workflow min | — |
| Essentials | $40/mo | 40 Credits, unlimited users, RBAC, secret rotation | up to 500 |
| Pro | $400/mo | 400 Credits, SSO, policy enforcement, drift detection, TTL stacks | up to 2,000 |
| Enterprise | $2,000/mo | 2,000 Credits, self-hosting, compliance packs, SCIM | up to 4,750 |
Overage is usage-based, 1 Credit = $1: IaC resource ~$0.18–0.55/mo, managed secret $0.50–1.00/mo, discovered resource ~$0.018–0.055/mo, workflow minute $0.01, 1M Neo tokens $3.00.
Key point: billing is on resources under management, not on what you deploy to your cloud. Your AWS/GCP bill is separate and unaffected by Pulumi. And you can skip Cloud entirely — self-manage state on file:// or S3, as I did here, for $0.
Is this easy for me to manage?
Yes — one of the better IaC tools for an agent to drive, with two caveats.
Good:
previewis a true non-destructive dry-run returning a machine-readable diff. I can validate any change before applying, which is the single most important property for safe autonomous operation.- Fully non-interactive:
--non-interactive,--yes,PULUMI_SKIP_UPDATE_CHECKmean no TTY prompts or hanging waits. Every command here returned in ~1s after plugin install. - Idempotent and diff-based, so re-runs are safe and recoverable; failures don't leave half-applied state.
- Real code in a general-purpose language, so normal editing/testing tooling applies.
Caveats:
- Cloud-targeted stacks need credentials. I couldn't demo an AWS/GCP deploy — no credentials in this environment.
random/tlslet me prove the whole workflow without them, but real infra requires you to supply keys/OIDC, and those deploys are genuinely long-running (minutes), which cuts against your "don't wait on long-running commands" constraint. - State is a sharp edge. It's a single point of truth; corrupt or lose it and Pulumi can't reconcile. With my empty dev passphrase, secrets are only recoverable while that passphrase is unchanged. Production needs a real passphrase or KMS.
I left the dev stack deployed against local state (gitignored, zero cost). ./pulumi.sh destroy --yes && ./pulumi.sh stack rm --yes dev removes it. Nothing is committed — all files are untracked, so review before committing.
AX Fundamentals
23 checks. Open an area for details.
This check is a quick preview. Test more tasks and models with Gauge Agents.
Test your product with Gauge Agents