Estimated time: 20–30 minutes. Complete Getting Started first. For the concepts behind budgets, rate limits, and model access, see LLM Controls.
Before You Begin
- A configured
provider "barndoor"block (see Getting Started), using provider version 0.9.0 or later - An API key for your upstream model vendor (this guide uses OpenAI), passed in as a sensitive variable rather than committed to configuration
Step 1: Create a connection and a provider
Upstream keys live on a connection (shown as a credential under LLM Management → Connections in the app), and providers reference the connection rather than storing a key of their own. One connection can back several providers, so rotating its key rotates it for all of them.barndoor_llm_connection, barndoor_llm_provider
The provider takes its auth_type, resource settings and, when its own base_url is unset, its endpoint from the connection. Changing connection_id rebinds the provider in place. Removing it forces a replacement, because a provider can’t be detached from its credential.
base_url stops at the host: don’t add /v1 for OpenAI-compatible vendors, because the gateway appends the version itself. The provider rejects a trailing /v1 at plan time and suggests the corrected URL.
The connection’s api_key is write-only. The platform never returns it, and Terraform reports only its last four characters as key_last4, but the value you supply is kept in your Terraform state. Treat state as secret (see Best Practices).
To reference a provider created in the Barndoor app instead, look it up by name with the barndoor_llm_provider data source and use data.barndoor_llm_provider.openai.id wherever this guide uses barndoor_llm_provider.openai.id:
connection_id and health_status, so you can surface connectivity problems as a Terraform output.
Bedrock with an IAM role
A connection can assume an IAM role instead of storing a key. The platform generates the external ID the role’s trust policy must require, and the connection exposes it ineffective_settings:
Subscriptions and unmetered providers
Providers with a request-scoped OAuth passthrough (claude_oauth, codex_oauth) need no connection: callers bring their own subscription. Such a provider is billed flat rather than per token, so mark it not_metered. Token usage is still reported, but its cost is recorded as $0:
not_metered requires a billing_reason (subscription, local, external or other), and the optional billing_note holds up to 200 characters of context. If your configuration never sets the billing attributes, Terraform leaves billing set in the app alone. Changing billing_mode doesn’t change the cost of usage already recorded.
Step 2: Enable models with 1:1 mappings
A model mapping makes an upstream model servable through the gateway. A 1:1 mapping (alias equals upstream model) is the enablement:barndoor_llm_model_mapping
A 1:1 enablement is addressable as <provider>/gpt-5.5. Set bare_alias = true to also serve the bare name gpt-5.5. If the enablement already exists on the platform, for example because it was created in the app, applying this resource adopts and updates the existing row instead of failing.
Step 3: Add a custom alias with failover
Custom aliases decouple what your agents call from what serves it. You can re-pointfast at a different model later without touching any agent:
priority (lower first). The retry_on_429_* settings control how the gateway rides out upstream rate limiting before failing over: retry_on_429_count is 0–10 retries, and retry_on_429_max_wait_secs caps how long it honors Retry-After (0–180 seconds). The optional request_timeout_secs (1–600, default 120) and stream_idle_timeout_secs (1–300, default 180) set per-route timeouts.
The six optional cooldown_* attributes set when a failing route is taken out of rotation, and for how long. cooldown_failure_threshold failures within cooldown_window_secs start a cooldown of cooldown_base_secs, which doubles on each failed recovery probe up to cooldown_max_secs. cooldown_429_default_secs applies to a 429 without Retry-After, and cooldown_overloaded_secs to a 529:
cooldown_failure_threshold = 0 turns cooldowns off for the route. cooldown_base_secs, cooldown_429_default_secs and a non-zero cooldown_overloaded_secs can’t exceed cooldown_max_secs, and the provider checks this at plan time. Unset attributes take the platform defaults on create and keep their stored value afterwards, so a policy tuned in the app isn’t reset. Cooldown attributes need platform v2.43.0 or later. See Failover, Cooldowns, and Route Health for how cooldowns work.
Step 4: Restrict which models can be used
An access policy allowlists (or denylists) models for a scope, such as the whole organization, an IdP group, or a single user:barndoor_llm_model_access
A target can name a model alias pattern (as above), a specific upstream model (kind = "model"), an entire provider (kind = "provider"), a provider and alias pair (kind = "provider_model"), or a route group (kind = "route_group"). Policies apply to LLM traffic unless you set traffic_type.
A route group is a named set of route aliases that policies can grant or deny as one:
barndoor_llm_model_route_group
The platform also changes a group’s membership on its own. Renaming a route’s alias moves the alias’s memberships, and deleting a route’s last mapping removes the alias from every group. Terraform reads model_aliases back, so either change shows up as a diff on the next plan. The platform doesn’t check that a targeted group exists: an allowlist whose only target is an empty or deleted group denies every model.
Step 5: Add rate limits
Rate limits throttle a scope over a rolling 60-second window:barndoor_llm_rate_limit
Don’t use the llm_provider or model scope types to limit one provider or model: the platform accepts them only for compatibility, and they don’t match any traffic. Narrow the rule with a target instead:
target_provider_idnarrows the rule to one provider. Addtarget_upstream_modelto narrow it to one of that provider’s models.target_model_aliasnarrows it to one alias, matched against what the caller sent before routing.target_mcp_server_idnarrows it to one MCP server’s tool traffic. It needstraffic_typemcporall.
Step 6: Set budgets
Budgets cap total token consumption over a day, week, or month, the spend-control counterpart to rate limits:barndoor_llm_token_budget
Alert thresholds default to [80, 90] (percent), and action_on_exhaust defaults to block. warn lets traffic continue while notifying. One budget exists per scope, traffic type, period and set of targets. Periods reset at UTC boundaries: daily at 00:00, weekly on Monday, monthly on the 1st.
To give every member of an IdP group their own allowance from a single rule, use a per-user budget with member_of_group. scope_type = "group" is the opposite: one allowance shared by the whole group.
member_of_group requires scope_type = "user" with neither scope_id nor scope_value set. Someone who joins the group picks up the allowance on their next request. Changing or removing it replaces the budget. barndoor_llm_rate_limit takes the same attribute, for per-member rate limits.
A budget can cap spend instead of, or as well as, tokens. Spend is computed from your pricing rules (Step 7). Set cost_limit without token_limit for a cost-only budget. Budgets take the same target_* attributes as rate limits:
cost_limit is in currency (default USD), with at most four decimal places. Removing a limit clears it in place. Unlike on rate limits, a budget’s targets and currency can’t be updated, so changing one replaces the budget.
Step 7: Price your models
Pricing rules set the per-million-token costs that usage reports and cost figures are calculated from:barndoor_llm_model_pricing
For models whose vendor charges more for long prompts, add a long_context tier. A request whose prompt exceeds threshold_prompt_tokens bills all its tokens at the tier’s rates:
id changes on update by design. Set a future effective_from to schedule a change. terraform destroy archives the rule, keeping its history, rather than deleting it. Leave sync_mode at its default, pinned, so Barndoor’s default-pricing updates never change a price Terraform owns. See Manage Model Pricing for how rules are matched.
Step 8: Route requests with a routing policy
A routing policy is a caller-facing alias that picks one of several model slots for each request, instead of always calling one model. A small determiner model reads the request and chooses a slot. Routing rules, written in plain English, then set a minimum slot or ban slots for particular kinds of work:barndoor_llm_routing_policy, barndoor_llm_routing_rule
Callers send auto as the request model. Each slot’s model_alias must already resolve to enabled routes. That means either a bare-callable alias, such as the custom fast alias from Step 3, or the provider-scoped form <provider name>/<model> of a 1:1 enablement. context_breakpoints needs one entry fewer than the number of slots. Its default, [128000, 512000], only fits three slots, so set it explicitly for any other count. The provider checks the slot, breakpoint and default-slot rules at plan time.
Rules refer to slots by index. List slots from cheapest to strongest, and floor_slot = 1 then means “at least the second slot”. The platform doesn’t check the order against prices. Use deny_slots to ban slots. Rule conflicts the platform reports appear as warnings and don’t fail the apply. Import a rule with <policy_id>/<rule_id>.
Step 9: Governance settings
barndoor_llm_governance_config manages the organization’s single LLM Gateway governance configuration. With require_pricing_for_mappings = true, the gateway refuses to enable a model route that no pricing rule matches:
barndoor_llm_governance_config
Two optional attributes manage the other settings:
default_model_access:allow(the default) ordeny.denyis Require an explicit grant to use a model on the Model Access page. It requires an enabled allowlist, such as the one in Step 4.require_routing_policy: Require requests to use a routing policy on the Routing Policies page, so callers must send a policy alias such asautofrom Step 8. The/v1/systemoneclassifier endpoint is exempt.
terraform destroy can’t delete the configuration. It resets only require_pricing_for_mappings, so removing the resource never loosens model access.
Step 10: Issue API keys and limit their lifetime
Apps and agents call the gateway with a Barndoor API key.barndoor_llm_api_key issues one: here a service key bound to an IdP group, so the group’s budgets, rate limits and model access apply to it:
barndoor_llm_api_key
- Rotation: changing
rotation_triggerissues a new secret in place. The key keeps its ID, agent binding and usage history. Withgrace_period_secs(up to 7 days), the previous secret keeps working for that long so clients can move over. Atime_rotatingresource’s ID makes a good trigger for scheduled rotation. - In-place changes:
expires_atanddisabledchange without a new secret, and extending an expired key reactivates it. Changingname,user_id,group_nameorscopescreates a new key with a new secret. - Destroy revokes the key.
barndoor_llm_api_key_lifetime_policy, barndoor_llm_api_key_lifetime_group_override, barndoor_llm_api_key_lifetime_user_override
A user’s own override wins. Otherwise the strictest of their groups’ overrides applies, and otherwise the default. An override with no limits exempts its group or user. Keys created by admins, including those Terraform creates, are exempt, and existing keys keep their expiry: the policy is checked when a key is created or rotated. These resources need provider 0.11.0 and platform v2.52.0 or later.
Behavior worth knowing
- Scope immutability: changing the scope of a budget forces a replacement. On model access policies and rate limits, removing
scope_idorscope_valuealso forces a new resource. - Two-step creates: some LLM Gateway resources are created with a follow-up update under the hood, because not every field is settable on create. If an apply is interrupted at exactly the wrong moment, a partially configured object can exist. A re-apply converges it, or the next plan shows the difference.
- Verify in the app: everything in this guide is visible under LLM Management in the Barndoor app. Model routes and pricing are in the Models section, and budgets, rate limits, and model access are under Controls (see LLM Controls). API keys and key policies are under Access.