Per-Workflow Charges
Fuzzball tracks resource usage for each workflow it runs and exposes the results through the CLI. Use this view to attribute compute, storage, and egress costs to specific workflows, jobs, and users — orthogonal to the cloud-provider billing covered in Tracking AWS Cloud Spend.
Every workflow accumulates four kinds of charges:
- Compute — node-hours of the substrate instance(s) running the workflow, priced per provisioner definition.
- Volume — GiB-hours that any storage volumes remained attached or held data on behalf of the workflow.
- Egress — bytes leaving the substrate node, bucketed into an egress class and priced per class.
- Ingress — bytes entering the substrate node. Tracked but not priced today.
Each charge is denominated in credits, the internal accounting unit. Catalog
rates are authored against published cloud-provider prices (e.g. AWS EC2 hourly
rates, AWS published data-egress rates), so the credit values track those
prices directly. There is no automatic conversion to local currency at present.
The fuzzball report charges workflow subcommand prints an itemized table of
every charge line tied to one workflow:
$ fuzzball report charges workflow <workflow_id>
CATEGORY | STAGE | BASIS | RATE | START | END | QUANTITY | CREDITS
compute | worker | t3.small | 0.0208 | 2026-06-03 01:29:33PM | 2026-06-03 01:29:37PM | 0.001226 hours | 0.00002551
egress | worker | cross_region_aws_service | 0.02 | 2026-06-03 01:29:33PM | 2026-06-03 01:29:37PM | 5285 bytes | 0.00000010
egress | worker | via_nat_to_internet | 0.135 | 2026-06-03 01:29:33PM | 2026-06-03 01:29:37PM | 14015 bytes | 0.00000176
ingress | worker | | (no pricing) | 2026-06-03 01:29:33PM | 2026-06-03 01:29:37PM | 2044528 bytes | —
volume | data | priced | 0.0001 | 2026-06-03 01:29:33PM | 2026-06-03 01:29:37PM | 0.001074 GiB-hours | 0.00000011The BASIS column shows the rate’s basis: instance type for compute,
egress class for egress,
storage provisioner name
for volume, and blank for ingress (no basis is tracked). Multi-class egress
renders one labeled row per class, so each class’s bytes and cost are
visible separately rather than aggregated.
Two values in the RATE column are not numeric:
(no pricing)— no rate exists in the catalog for that class. Credits show as—(cannot be computed). Ingress is always in this state today.(zero rate)— the catalog does have an entry but its rate is 0. Credits compute explicitly to 0.00, distinguishing “intentionally free” (e.g. traffic through an S3 gateway endpoint) from “unpriced.”
CREDITS = RATE × QUANTITY for each row, with unit conversion folded into
the rate’s denominator (per hour, per GiB, per GiB-hour, etc.).
The volume subcommand prints the lifetime accumulated charge on a single
storage volume, addressed by volume name, volume ID, or
provisioner-name + volume-name:
$ fuzzball report charges volume <volume_name>
$ fuzzball report charges volume <volume_id>
$ fuzzball report charges volume <provisioner_name> <volume_name>A bare volume name is matched across every provisioner you can see. If the
same name exists on more than one provisioner, the command lists the
matching provisioner/volume pairs and asks you to qualify the name with
the two-argument form.
Useful when a persistent volume has carried multiple workflows and you want the total spend on that volume rather than per-workflow attribution.
The table lists only the workflow attachment windows; the total volume cost below it covers the volume’s full lifetime, including time it was not attached to any workflow.
For higher-level rollups, fuzzball report charge-summary collapses each
workflow’s per-line detail into one summary row per (workflow, category)
combination. The workflow subcommand shows the summary for a single
workflow; list returns many summaries with filter and pagination options:
$ fuzzball report charge-summary workflow <workflow_id>
$ fuzzball report charge-summary list \
[--group <group_id>] [--user <user_id>] [--cluster <cluster_id>] \
[--start-from <RFC3339>] [--start-until <RFC3339>] \
[--page-size N] [--page-token TOKEN]The --group flag is repeatable. The --cluster filter only applies on
Federate deployments
where multiple clusters are aggregated.
The aggregate subcommand collapses everything in the caller’s scope into a
single total, with the same filter flags:
$ fuzzball report charge-summary aggregate \
[--group <group_id>] [--user <user_id>] [--cluster <cluster_id>] \
[--start-from <RFC3339>] [--start-until <RFC3339>]--start-from/--start-until bound the aggregate two ways, depending on what the usage
is attached to. Compute, egress, and workflow-attached storage are reported
for every workflow that started in the period, so a workflow running past
--start-until still contributes its whole run to the period it began in. Usage
that belongs to no single workflow – persistent volumes and object cache
entries – is instead clipped to the period, because those lifetimes span
many reports and counting each one in full every time would repeat all prior
periods’ usage.
Objects in the object cache occupy
storage from the moment they are cached until they are purged (TTL expiry,
disk-pressure eviction, or explicit delete). That usage is metered as
GiB-hours against the object’s namespace owner — the group for group/
objects, the user for user/ objects — independent of which workflows read
the object.
Object cache usage appears on fuzzball report charge-summary aggregate as
its own Object cache line. It is priced by the cluster-wide objectCache
rate in central config:
objectCache:
costPerGbHour: 0.002
The rate is applied at report time, so changing it re-prices all displayed
object cache usage, including past periods. With no rate configured, the
GiB-hours quantity is still reported but contributes no credits. When a
report is bounded with --start-from/--start-until, each object’s cached window is
clipped to the requested period.
fuzzball report auto-narrows by the caller’s role:
- Regular users see only their own workflows.
- Group owners see workflows belonging to groups they own.
- Organization owners see all workflows in their organization.
The optional --group, --user, and --cluster filters narrow within the
caller’s already-permitted scope — they cannot widen it. A regular user
filtering by --user for someone else’s workflows will simply see no
results.
When an AWS substrate runs a workflow, each destination address the workload egresses to is classified by walking the source node’s VPC route table. The class determines the rate. Default classes shipped in the AWS egress catalog:
| Class | Default rate (credits/GiB) | Triggered when… |
|---|---|---|
intra_az_local | 0.00 | Destination is a pod/service in the source node’s own subnet |
intra_az_other_subnet | 0.00 | Destination is in the source’s AZ but a different VPC subnet |
cross_az_intra_vpc | 0.01 | Destination is in the same VPC but a different AZ |
via_s3_gateway_endpoint | 0.00 | Destination resolves to S3 in-region and the VPC has an S3 gateway endpoint |
via_dynamodb_gateway_endpoint | 0.00 | Same, for DynamoDB |
via_privatelink | 0.01 | Destination resolves to a configured PrivateLink interface endpoint |
cross_region_aws_service | 0.02 | Destination is an AWS-published service IP in a region different from the cluster |
via_nat_to_internet | 0.135 | Default route via NAT gateway (most generic internet egress) |
via_igw_to_internet | 0.09 | Default route via Internet Gateway (substrate in a public subnet) |
unknown | 0.135 | Source-instance placement or VPC topology could not be resolved |
The unknown class is intentionally priced at the most expensive realistic
rate so misclassifications surface as conservative billing rather than free
traffic.
On non-AWS deployments (on-prem, Slurm/PBS) the matcher is source-blind: it
applies operator-declared CIDR rules from
central config instead of walking VPC routing
state. The class names emitted in that mode are whatever the operator named in
those rules. Rules may use any prefix length, down to a single host (/32),
so a specific license server or storage endpoint can carry its own class and
rate.
All fuzzball report subcommands honor the global --output flag:
--output table(default) — human-readable.--output json— machine-readable. Useful for piping into downstream spend-attribution tools.
YAML output is not supported for report commands; the data is tabular by nature and YAML adds no value over JSON.