Setup

Cost analysis is off by default. Turning it on has two parts, both handled by a single command:

  1. A pricing service, shipped inside the server image, that prices each resource. Stategraph calls it via STATEGRAPH_PRICING_SERVICE_URL.
  2. A price book, the cloud pricing data the service looks resources up in, loaded into a cloud_pricing PostgreSQL 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:

  1. Downloads the latest price book and loads it into the cloud_pricing database, creating it if it does not exist.
  2. Starts the pricing service, listening on port 8090 inside the container.
  3. Sets STATEGRAPH_PRICING_SERVICE_URL and 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 any PRICING_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-enable turns it back on.
  • The price book lives in the cloud_pricing database, so it persists across restarts. Load it once with load-pricing-data (and again to refresh). If you run a managed or external database, point PRICING_DB_* at it and make sure the database user can create cloud_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