Budget Configuration Guide
Overview
Section titled “Overview”Budgets allow you to set spending limits for your infrastructure and receive alerts when costs exceed defined thresholds. By configuring budgets in FinFocus, you can proactively manage cloud spending and prevent unexpected overruns before they happen.
This guide covers how to define monthly budgets, set up alerts for actual and projected costs, and integrate budget checks into your CI/CD pipelines.
Target Audience: End Users, DevOps Engineers, FinOps Practitioners
Prerequisites:
- FinFocus CLI installed
- Pulumi project configured with cost data
Learning Objectives:
- Configure monthly budget amounts and currencies
- Set up alerts for actual vs. forecasted costs
- Integrate budget enforcement into CI/CD workflows
- Troubleshoot common budget configuration issues
Estimated Time: 10 minutes
Table of Contents
Section titled “Table of Contents”- Quick Start
- Configuration Reference
- Examples
- Scoped Budgets
- Budget Notifications
- Troubleshooting
- See Also
Quick Start
Section titled “Quick Start”Get started with budgets in under 5 minutes.
Step 1: Configure Budget
Section titled “Step 1: Configure Budget”Create or edit your ~/.finfocus/config.yaml to define a budget:
# yaml-language-server: $schema=https://rshade.github.io/finfocus/schemas/config.jsoncost: budgets: amount: 500.00 currency: USD period: monthly alerts: - threshold: 80 type: actual - threshold: 100 type: forecastedStep 2: Run Cost Analysis
Section titled “Step 2: Run Cost Analysis”Run a cost projection to see how your infrastructure compares to the budget:
finfocus cost projected --pulumi-json plan.jsonStep 3: Review Output
Section titled “Step 3: Review Output”Expected Output:
Budget: $500.00 (75% used)[=====================>......] $375.00 / $500.00
RESOURCE ADAPTER MONTHLY CURRENCY NOTESaws:ec2/instance:Instance aws-spec $375.00 USD t3.xlarge
Figure 1: Budget display showing usage against defined threshold.
What’s Next?
Configuration Reference
Section titled “Configuration Reference”Complete reference for all budget configuration options.
File Location
Section titled “File Location”Configuration is stored in ~/.finfocus/config.yaml.
Schema Reference
Section titled “Schema Reference”For IDE autocomplete and validation, add this comment to your config file:
# yaml-language-server: $schema=https://rshade.github.io/finfocus/schemas/config.jsonConfiguration Options
Section titled “Configuration Options”| Option | Type | Default | Required | Description |
|---|---|---|---|---|
amount |
number | - | Yes | Budget amount in specified currency |
currency |
string | "USD" |
No | ISO 4217 currency code (USD, EUR, GBP) |
period |
string | "monthly" |
No | Budget period (daily, weekly, monthly, yearly) |
alerts |
list | [] |
No | Alert thresholds (see Alerts Options below) |
Alerts Options
Section titled “Alerts Options”| Option | Type | Default | Required | Description |
|---|---|---|---|---|
threshold |
number | - | Yes | Percentage of budget (1-100) |
type |
string | "actual" |
No | Alert type (actual, forecasted) |
notifications |
list | [] |
No | Destinations to notify (see Notifications) |
Environment Variables
Section titled “Environment Variables”Override configuration with environment variables:
| Variable | Description | Example |
|---|---|---|
FINFOCUS_BUDGET_AMOUNT |
Override budget amount | 500.00 |
FINFOCUS_BUDGET_CURRENCY |
Override currency | EUR |
FINFOCUS_BUDGET_EXIT_ON_THRESHOLD |
Exit process when budget threshold exceeded | true |
FINFOCUS_BUDGET_EXIT_CODE |
Exit code to use when enforcement triggers | 2 |
FINFOCUS_NOTIFY |
Send budget notifications when --notify is not set |
true |
See Configuration Reference for complete details.
Examples
Section titled “Examples”Practical examples for common budget scenarios.
Example 1: Single Budget Threshold
Section titled “Example 1: Single Budget Threshold”Use Case: Simple enforcement to ensure costs don’t exceed a hard limit.
Configuration:
cost: budgets: amount: 1000.00 currency: USD period: monthly alerts: - threshold: 100 type: actualUsage:
finfocus cost projected --pulumi-json plan.jsonExplanation:
This configuration sets a hard limit of $1000/month. If actual costs exceed this amount, the CLI will display a warning and exit with a non-zero status code (if configured).
See complete example.
Example 2: Multiple Alert Thresholds
Section titled “Example 2: Multiple Alert Thresholds”Use Case: Progressive warnings to catch cost creep early.
Configuration:
cost: budgets: amount: 2000.00 currency: USD alerts: - threshold: 50 type: actual # Early warning at 50% - threshold: 80 type: forecasted # Warning if projected to reach 80% - threshold: 100 type: actual # Critical alert at 100%Explanation:
This setup provides early visibility. You’ll get notified when you hit 50% of budget, if you’re projected to hit 80%, and finally when you breach 100%.
See complete example.
Example 3: CI/CD Integration
Section titled “Example 3: CI/CD Integration”Use Case: Fail build pipelines when infrastructure changes exceed budget.
Configuration:
cost: budgets: amount: 500.00 alerts: - threshold: 100 type: forecastedUsage:
# In your CI pipeline scriptfinfocus cost projected --pulumi-json plan.json || { echo "Budget exceeded!" exit 1}Explanation:
By using type: forecasted at 100% threshold, FinFocus checks if the new infrastructure plan will push total costs
over budget. If yes, it returns a non-zero exit code, stopping the deployment.
See complete example.
Scoped Budgets
Section titled “Scoped Budgets”Note: FinFocus supports two budget configuration styles. The Quick Start examples above use the flat format (
cost.budgets.amount) for simple single-budget setups. The scoped format below usescost.budgets.globalwith nestedproviders,tags, andtypessections for multi-level budget control. Both are valid; use flat for simplicity or scoped when you need per-provider/tag/type breakdowns.
Define budgets at multiple levels for granular cost control: global, per-provider, per-tag, and per-resource-type.
Provider Budgets
Section titled “Provider Budgets”Track and limit spending per cloud provider (AWS, GCP, Azure).
Configuration:
cost: budgets: # Global budget applies to all resources (required when scopes defined) global: amount: 5000.00 currency: USD period: monthly alerts: - threshold: 80 type: actual
# Per-provider budgets providers: aws: amount: 3000.00 gcp: amount: 2000.00 azure: amount: 1000.00Usage:
# View all budgets including provider breakdownfinfocus cost projected --pulumi-json plan.json
# Filter to show only provider budgetsfinfocus cost projected --pulumi-json plan.json --budget-scope=provider
# Filter to a specific providerfinfocus cost projected --pulumi-json plan.json --budget-scope=provider=awsExample Output:
BUDGET STATUS═════════════════════════════════════════════════════════════
GLOBAL Budget: $5,000.00 | Spend: $3,250.00 (65.0%) ████████████████████░░░░░░░░░░ OK
BY PROVIDER─────────────────────────────────────────────────────────────── aws Budget: $3,000.00 | Spend: $2,100.00 (70.0%) OK gcp Budget: $2,000.00 | Spend: $1,150.00 (57.5%) OK azure Budget: $1,000.00 | Spend: $0.00 (0.0%) OK
Overall Health: OKKey Points:
- Provider names are case-insensitive (
aws,AWS,Awsall match) - Provider is extracted from resource type (e.g.,
aws:ec2/instance→aws) - All provider budgets must use the same currency as the global budget
- Each resource’s cost counts toward both its provider budget AND the global budget
Tag Budgets
Section titled “Tag Budgets”Track costs by resource tags (e.g., team:platform, env:prod) with priority-based allocation.
Configuration:
cost: budgets: # Global budget applies to all resources (required when scopes defined) global: amount: 10000.00 currency: USD period: monthly
# Per-tag budgets with priority ordering tags: - selector: 'team:platform' priority: 100 amount: 3000.00 - selector: 'team:backend' priority: 100 amount: 2500.00 - selector: 'env:prod' priority: 50 amount: 5000.00 - selector: 'cost-center:*' priority: 10 amount: 1000.00Usage:
# View all budgets including tag breakdownfinfocus cost projected --pulumi-json plan.json
# Filter to show only tag budgetsfinfocus cost projected --pulumi-json plan.json --budget-scope=tagExample Output:
BUDGET STATUS═════════════════════════════════════════════════════════════
GLOBAL Budget: $10,000.00 | Spend: $6,500.00 (65.0%) ████████████████████░░░░░░░░░░ OK
BY TAG─────────────────────────────────────────────────────────────── team:platform Budget: $3,000.00 | Spend: $2,100.00 (70.0%) OK team:backend Budget: $2,500.00 | Spend: $1,500.00 (60.0%) OK env:prod Budget: $5,000.00 | Spend: $4,200.00 (84.0%) WARNING
Overall Health: WARNINGTag Selector Patterns:
| Pattern | Description | Example Match |
|---|---|---|
key:value |
Exact match on tag key and value | team:platform matches resources with team=platform |
key:* |
Wildcard match on any value for the key | env:* matches env=prod, env=dev, env=staging |
Priority-Based Allocation:
When a resource matches multiple tag budgets, cost is allocated to the highest priority budget only:
- Higher priority values take precedence (100 > 50 > 10)
- If multiple budgets share the same priority, the first alphabetically wins
- A warning is emitted when priority ties occur
Configuration Tips:
- Use specific selectors (
team:platform) for known teams - Use wildcards (
cost-center:*) as catch-all budgets with lower priority - Ensure higher-priority budgets are more specific to avoid allocation conflicts
Resource Type Budgets
Section titled “Resource Type Budgets”Track and limit spending per resource type (e.g., aws:ec2/instance, gcp:compute/instance).
Configuration:
cost: budgets: # Global budget applies to all resources (required when scopes defined) global: amount: 10000.00 currency: USD period: monthly
# Per-resource-type budgets types: 'aws:ec2/instance': amount: 2000.00 'aws:rds/instance': amount: 3000.00 'gcp:compute/instance': amount: 1500.00Usage:
# View all budgets including type breakdownfinfocus cost projected --pulumi-json plan.json
# Filter to show only resource type budgetsfinfocus cost projected --pulumi-json plan.json --budget-scope=typeExample Output:
BUDGET STATUS═════════════════════════════════════════════════════════════
GLOBAL Budget: $10,000.00 | Spend: $5,500.00 (55.0%) █████████████████░░░░░░░░░░░░░░ OK
BY TYPE─────────────────────────────────────────────────────────────── aws:ec2/instance Budget: $2,000.00 | Spend: $1,200.00 (60.0%) OK aws:rds/instance Budget: $3,000.00 | Spend: $2,700.00 (90.0%) CRITICAL
Overall Health: CRITICALKey Points:
- Resource types use exact matching (case-sensitive)
- Type is extracted from Pulumi resource type (e.g.,
aws:ec2/instance:Instance→aws:ec2/instance) - All type budgets must use the same currency as the global budget
- Each resource’s cost counts toward its type budget AND the global budget
- Unconfigured resource types do not appear in the BY TYPE section
Budget Notifications
Section titled “Budget Notifications”An alert threshold can send a message to Slack or to any HTTPS endpoint when a cost command finds that the threshold was crossed. Notifications are opt-in per run, so local runs stay quiet while CI sends.
Configure Destinations
Section titled “Configure Destinations”Add notifications to any alert, in any scope (global, providers.<name>,
tags[], types.<pattern>). Put destinations that use secrets in the global
~/.finfocus/config.hujson:
{ "cost": { "budgets": { "global": { "amount": 100, "currency": "USD", "alerts": [ { "threshold": 80, "type": "actual", "notifications": [ {"type": "slack", "url": "${FINFOCUS_NOTIFY_SLACK_URL}", "channel": "#finops-alerts"}, { "type": "webhook", "url": "https://api.example.com/budget-alert", "method": "POST", "headers": {"Authorization": "Bearer ${FINFOCUS_NOTIFY_API_TOKEN}"} } ] } ] } } }}| Field | Applies to | Required | Description |
|---|---|---|---|
type |
all | Yes | slack (incoming webhook) or webhook (generic HTTPS endpoint) |
url |
all | Yes | https:// URL with a host, or a ${FINFOCUS_NOTIFY_*} reference |
channel |
slack |
No | Channel override sent with the message |
method |
webhook |
No | POST (default) or PUT |
headers |
webhook |
No | Extra request headers; values may use ${FINFOCUS_NOTIFY_*} |
Check the file with finfocus config validate. Validation rejects unknown
types, http:// URLs, channel on a webhook, method or headers on a Slack
destination, and any variable that does not start with FINFOCUS_NOTIFY_.
Slack apps bind each incoming webhook to one channel, so Slack may ignore
channel. Create one webhook per channel when you need several.
Send Notifications
Section titled “Send Notifications”A run sends only when you pass --notify (on cost projected, cost actual,
and overview) or set FINFOCUS_NOTIFY=true. An explicit --notify=false
wins over the variable. An invalid FINFOCUS_NOTIFY value prints a warning
and counts as false.
When a run does not opt in and a crossed threshold has destinations, nothing is sent and stderr shows one hint:
budget notifications for 1 exceeded threshold(s) were not sent; pass --notify or set FINFOCUS_NOTIFY=trueEvery opted-in run that crosses a threshold sends again. FinFocus keeps no notification history between runs, and sends one message per crossed threshold and destination.
CI Recipe
Section titled “CI Recipe”env: FINFOCUS_NOTIFY: "true" FINFOCUS_NOTIFY_SLACK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}steps: - run: finfocus cost projected --pulumi-json plan.jsonWrite the destinations into the global config from the workflow, for example
with a step that creates $FINFOCUS_HOME/config.hujson. A pull request
cannot change that step for runs that receive secrets.
Preview With Dry Run
Section titled “Preview With Dry Run”finfocus cost projected --pulumi-json plan.json --notify --dry-rundry-run: would notify slack for global budget, 80% actual thresholdNothing is sent. Each line names the destination type and threshold, never the URL.
What the Receivers Get
Section titled “What the Receivers Get”Slack receives a message with the budget, current (or forecasted) spend and
percentage, the threshold, and the status. A webhook destination receives a
JSON event:
{ "event": "budget.threshold.exceeded", "timestamp": "2026-10-04T15:04:05Z", "budget": {"name": "provider:aws", "scope": "provider", "scope_key": "aws", "amount": 50, "currency": "USD", "period": "monthly"}, "threshold": {"percentage": 100, "type": "forecasted", "value": 50}, "current": {"spend": 60, "percentage": 120}, "metadata": {"source": "finfocus", "version": "0.5.0"}}budget.name is global or <scope>:<scope_key>. For a forecasted alert,
current holds the forecasted spend and percentage. Content-Type is
application/json unless a configured header sets it.
Delivery and Failures
Section titled “Delivery and Failures”-
Destinations are sent concurrently, each with a 10-second time limit.
-
URLs must use HTTPS. A URL built from a variable is checked after expansion. Redirects are never followed; a 3xx response is a failure.
-
A failure (error status, timeout, unset variable) prints a warning on stderr and never changes the command’s output or exit code:
warning: slack notification for global budget (80% actual) failed: unexpected response status: status 500 -
Warnings and logs never contain a URL, a variable’s value, a secret header value, or a response body. A literal header value is treated as secret when it is at least 8 characters long or the header is
Authorization,Proxy-Authorization,Cookie, or a name containingtoken,key, orsecret.config getandconfig listshow those values as[REDACTED], except a value that is only a${NAME}reference. -
Nothing about notifications is written to stdout, so
--output jsonand--output ndjsonstay parseable. -
Results with mixed currencies skip budget evaluation, so no notification is sent.
Security: Project Config and Pull Requests
Section titled “Security: Project Config and Pull Requests”A project config ($PROJECT/.finfocus/config.hujson) is committed, so a pull
request can edit it. Three rules keep CI secrets out of a destination that a
pull request controls:
- Only
${FINFOCUS_NOTIFY_*}variables expand, in any config. A reference such as${AWS_SECRET_ACCESS_KEY}or${GITHUB_TOKEN}fails validation and is never read. - Destinations in a project config never expand variables.
finfocus config validate(which also checks the resolved project file when run without--file), the cost commands, andoverview --notifyreject a${...}reference there with a hint to move the destination to the global config. This covers a legacy.finfocus/config.yamltoo. If one reaches a run, that destination is skipped with a warning. - When FinFocus cannot find a home directory and falls back to
./.finfocusfor its global config, that file is treated as a project config, because it may be committed.
A project-config destination with only literal values still sends. A pull
request can therefore point the budget event at a host it chooses. The event
holds budget figures only, but do not set FINFOCUS_NOTIFY on runs for
untrusted pull requests.
Budget Visibility in Overview
Section titled “Budget Visibility in Overview”The finfocus overview command shows budget status differently per output mode:
- Interactive TUI: Budget footer with health badge loads asynchronously below the resource table. Press Enter on a resource for per-budget detail with forecasts and triggered alerts.
- JSON (
--output json): Full budget data in the top-levelbudgetsarray. - NDJSON (
--output ndjson): Budgets are excluded — NDJSON is resource-scoped and budgets are stack-scoped. - Plain (
--plain): Budget status is not rendered in the table. Use--exit-on-thresholdfor CI/CD enforcement.
See overview command — Budget Status for the complete visibility matrix and flag reference.
Troubleshooting
Section titled “Troubleshooting”Common issues and solutions for budget configuration.
Issue: Alerts not triggering
Section titled “Issue: Alerts not triggering”Symptoms:
- Costs clearly exceed budget but no alert is shown
- Exit code is 0 despite overage
Cause:
- Mismatch between
currencyin config and cost data - Using
actualalert type for projected costs (or vice versa)
Solution:
Check your currency matches your cloud provider data:
currency: USD # Ensure this matches plugin outputAnd ensure you’re using the right alert type. Use forecasted for cost projected commands.
Issue: Schema validation errors
Section titled “Issue: Schema validation errors”Symptoms:
- IDE highlights config properties in red
unknown propertyerrors
Solution:
Ensure you have the correct schema directive and your indentation is correct:
# yaml-language-server: $schema=https://rshade.github.io/finfocus/schemas/config.jsonSee Also
Section titled “See Also”Related Guides:
- Recommendations Guide - Cost optimization suggestions
- Accessibility Guide - Terminal display options
CLI Reference:
- overview - Unified cost dashboard with budget status
- cost projected - Estimate projected costs
- cost recommendations - Display recommendations
Configuration Reference:
- Budget Configuration - Complete option reference
Examples:
- Budget Examples - Runnable configuration files
Last Updated: 2026-10-04 FinFocus Version: v0.3.4 Feedback: Open an issue to improve this guide