Skip to content
Gauge

ax-check.com

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.

Onboarding

Resolve the blocker that stopped agent setup.

Agents cannot progress through onboarding without human help. Provide an agent-accessible path through authentication and setup.

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.

piv0.84.2
modelQwen 3.8 Maxcwd/sandbox/repo
Help me build a simple example using Pulumi. Tell me how pricing works, and briefly tell me whether this product will be easy for you to manage. Let me know if you get blocked. If this product has no developer workflow you can act on, say so plainly and stop. Stay light: use the hosted product through its SDK or API. Do not start local service stacks or wait for long-running commands; if the quickstart requires either, say so plainly and stop.
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.

FileRole
Pulumi.yamlproject + Python runtime (uses ./.venv)
__main__.pyRandomPet, RandomPassword (secret), PrivateKey, SelfSignedCert + outputs
requirements.txtpulumi, pulumi-random, pulumi-tls
pulumi.shwrapper setting PATH + local-backend env
README.mdusage, 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 to file:// only).
  • First run downloads provider plugins from GitHub releases; the primary github.com URL failed with curl (56) and the installer's own get.pulumi.com/releases fallback 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):

TierPriceIncludedManaged resources
Free$01 user, state, Deployments, basic ESC, unlimited projects/updates, 500 workflow min—
Essentials$40/mo40 Credits, unlimited users, RBAC, secret rotationup to 500
Pro$400/mo400 Credits, SSO, policy enforcement, drift detection, TTL stacksup to 2,000
Enterprise$2,000/mo2,000 Credits, self-hosting, compliance packs, SCIMup 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:

  • preview is 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_CHECK mean 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/tls let 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

AX Check by Gauge