Setup
Cost analysis is off by default. Turning it on has two parts, both handled by a single command:
- A pricing service, shipped inside the server image, that prices each resource. Stategraph calls it via
STATEGRAPH_PRICING_SERVICE_URL. - A price book, the cloud pricing data the service looks resources up in, loaded into a
cloud_pricingPostgreSQL database.
Quick Start (Docker)
From your deployment directory, run the bundled pricing-enable helper inside the server container:
docker compose exec server pricing-enable
This does everything needed to switch cost analysis on:
- Downloads the latest price book and loads it into the
cloud_pricingdatabase, creating it if it does not exist. - Starts the pricing service, listening on port
8090inside the container. - Sets
STATEGRAPH_PRICING_SERVICE_URLand restarts the server so the change takes effect.
The first run downloads a large dataset (several hundred MB compressed, millions of prices), so allow a few minutes and make sure the database volume has room for it. The command is idempotent: re-running it reloads the price book and leaves the service running.
Successful output ends with:
==> Loaded 3621618 products
==> Pricing enabled (cost surfaces at http://localhost:8090)
Kubernetes and ECS
The pricing service and price book are baked into the same server image, so cost works the same way on Kubernetes and ECS. Two things change: how you run the bundled commands, and where you set the configuration so it survives a pod or task replacement.
Turn it on
Run pricing-enable inside the running server container.
Kubernetes (Helm release stategraph in namespace stategraph):
kubectl exec -n stategraph deploy/stategraph -- pricing-enable
ECS, with ECS Exec enabled on the service:
CLUSTER=$(terraform output -raw ecs_cluster_name)
SERVICE=$(terraform output -raw ecs_service_name)
TASK=$(aws ecs list-tasks --cluster "$CLUSTER" --service-name "$SERVICE" \
--query 'taskArns[0]' --output text)
aws ecs execute-command --cluster "$CLUSTER" --task "$TASK" \
--container stategraph --interactive --command "/usr/local/bin/pricing-enable"
This loads the price book, starts the pricing service, and switches cost on for the running container, exactly as on Docker. Refresh later by running load-pricing-data the same way.
Make it durable
pricing-enable configures the running container, but a pod or task that gets replaced starts from the image again with the pricing service off. For a setup that survives restarts:
- Set
STATEGRAPH_PRICING_SERVICE_URL=http://localhost:8090(and anyPRICING_DB_*overrides) in your Deployment or Task Definition environment, alongside the server's other variables — in your Helm values for Kubernetes, or the container environment for ECS. - Enable the pricing service so it starts with the container. It ships disabled in the image; the bundled Compose enables it at boot, and your Helm values or task definition should do the same. If a new container comes up with cost off, re-running
pricing-enableturns it back on. - The price book lives in the
cloud_pricingdatabase, so it persists across restarts. Load it once withload-pricing-data(and again to refresh). If you run a managed or external database, pointPRICING_DB_*at it and make sure the database user can createcloud_pricing, or pre-create the database.
Verify
Confirm cost analysis is enabled:
curl -H "Authorization: Bearer $STATEGRAPH_API_KEY" \
http://localhost:8080/api/v1/capabilities
{ "costs": { "enabled": true } }
If enabled is false, the pricing service is not configured or is unreachable, and POST .../costs/calculate returns 503. Once enabled, produce the first estimate for a state with POST .../costs/calculate, or wait for the next scheduled run.
Refreshing the Price Book
Cloud prices change, so reload the price book periodically (monthly, for example). Re-run pricing-enable, or just the loader:
docker compose exec server load-pricing-data
The load is atomic: it builds the new price book in the background and swaps it in, so cost analysis keeps serving the previous prices until the new data is ready.
Configuration
pricing-enable sets STATEGRAPH_PRICING_SERVICE_URL for you, and the pricing service connects to the same PostgreSQL the server uses. You only need the variables below to customize the setup, such as putting the price book in a separate database or mirroring the price-book download.
| Variable | Default | Description |
|---|---|---|
STATEGRAPH_PRICING_SERVICE_URL |
— | Endpoint of the pricing service (set automatically by pricing-enable). Without it, cost analysis is disabled. |
STATEGRAPH_PRICING_DEFAULT_REGION |
us-east-1 |
Region assumed when a resource does not specify one. |
STATEGRAPH_COST_SCHEDULE_HOURS |
24 |
How often snapshots are recomputed on a schedule. |
STATEGRAPH_COST_EVENT_DEBOUNCE_HOURS |
6 |
Minimum gap before an apply triggers another recompute. |
STATEGRAPH_COST_PRICING_CALL_TIMEOUT_SECONDS |
30 |
Timeout for a single pricing-service call. |
PRICING_DB_HOST / PRICING_DB_PORT |
server's database | Host and port of the cloud_pricing database. Defaults match the bundled Compose database. |
PRICING_DB_USER / PRICING_DB_PASSWORD |
server's database | Credentials for the cloud_pricing database. |
PRICING_DB_NAME |
cloud_pricing |
Name of the price-book database. |
PRICING_DATA_URL |
Stategraph's hosted price book | Override the price-book download source, such as your own mirror for air-gapped installs. |
See Environment Variables for the full server configuration reference.
Air-Gapped Installs
If the server cannot reach the internet to download the price book, host the products.csv.gz file on a reachable URL or internal mirror, set PRICING_DATA_URL to it, and run load-pricing-data. To keep the price book in a database separate from the server's, set the PRICING_DB_* variables to point the pricing service and loader at it.
Next Steps
- State & Resource Cost - Price a state and read the breakdown
- Cost Attribution - Roll up and attribute spend across your tenant