Skip to main content
A Claude apps gateway deployment is configured by one YAML file, conventionally gateway.yaml. The file defines everything the gateway does: where it listens, how developers sign in, where inference goes, and which policies and telemetry apply. This page is the reference for every option in that file. To write your first one, start from the quickstart, which builds a minimal working config and runs it. Once you have a config you’re happy with, the deployment guide covers containerizing and hosting it on Kubernetes, Cloud Run, or your own platform. The gateway reads the file once, at startup, with claude gateway --config /path/to/gateway.yaml. Every option is validated against a schema at boot, so a malformed config fails at start with a field-level error rather than at first use. The complete example at the end of this page exercises every section.

File structure

Five sections are required. Every other section is optional, and an omitted section takes its defaults. Unknown keys fail boot, so a typo surfaces as a named error rather than a silently ignored setting. Required sections:
  • listen: bind address, public URL, TLS termination
  • oidc: your identity provider (IdP), including issuer, client, claim mapping, and who may sign in
  • session: the bearer tokens the gateway mints, with secret and lifetime
  • store: PostgreSQL, for device grants and rate-limit counters
  • upstreams: where inference goes, whether Anthropic, Amazon Bedrock, Claude Platform on AWS, Google Cloud’s Agent Platform, or Microsoft Foundry
Optional sections:
  • admin: Admin API auth and retention for spend limits
  • enforcement: spend-limit fail-open or fail-closed behavior
  • models and auto_include_builtin_models: admin-curated model list and per-upstream IDs
  • managed: managed settings policies by IdP group
  • telemetry: OTLP forwarding to your observability stack
  • access_control, limits, timeouts, rate_limits: IP allow/deny, request size caps, upstream time-to-first-byte, and per-IP sign-in limits

Secret expansion

Don’t write secrets such as client_secret, jwt_secret, or postgres_url directly in gateway.yaml. Reference them with one of the forms below, and the gateway resolves the value at boot from an environment variable or a file:

Required sections

listen

The listen block controls where the gateway serves: the bind address and port, the externally visible origin, and optional TLS termination.

oidc

The oidc block connects the gateway to your identity provider and decides who can sign in. It names the issuer and OAuth client, maps the claims that carry email and groups, and restricts sign-in by email domain or group. OpenID Connect (OIDC) is the SSO protocol the gateway uses with your identity provider; see Identity provider setup for what to register on the IdP side.

session

The session block shapes the bearer tokens the gateway mints after sign-in: the secret that signs them and how long they live.

store

The store block points the gateway at its PostgreSQL database, which holds device grants and rate-limit counters. For local development, point postgres_url at a throwaway Postgres container, for example docker run --rm -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust postgres.

upstreams

upstreams is an ordered list. The gateway forwards inference to the first upstream that resolves the requested model. On 5xx, 429, 401, 403, 404, or timeout it fails over to the next; other 4xx doesn’t, because those errors are attributable to the request rather than the upstream. A 401 or 403 means the gateway’s own credential failed against that upstream, and a 404 means that upstream doesn’t serve the requested model, so a later upstream in the list still can. Failover on 404 requires gateway v2.1.198 or later. Earlier releases returned the first 404 to the client even when a later upstream in the list served the model. Multiple upstreams of the same provider must set a distinct name:. Amazon Bedrock, Claude Platform on AWS, Google Cloud’s Agent Platform, and Microsoft Foundry clients are built once at startup, and their SDKs refresh credentials internally, so rotating cloud credentials doesn’t require a restart. Static Anthropic API keys and bearers are read at startup; see Anthropic API.

Anthropic API

The minimal Anthropic upstream is an API key from the Claude Console:
The two credential forms differ in the header they send:
  • api_key: sends x-api-key. Rotate it in the Claude Console and update the env var.
  • oauth_token: sends Authorization: Bearer. Use the bearer form when your org issues short-lived tokens instead of long-lived API keys. The bearer is read once at startup, so refresh by remounting the secret and restarting.
Instead of a static key or bearer, you can use Workload Identity Federation. Create a federation rule by following the Workload Identity Federation guide, then mount your workload’s OIDC JWT as a file, such as a Kubernetes projected service-account token or a CI platform’s id-token. The gateway exchanges the JWT for a short-lived bearer and refreshes it automatically. The token file is re-read on every exchange, so rotated projected tokens are picked up without a restart.

Amazon Bedrock

For the client-side Amazon Bedrock deployment that the gateway replaces or fronts, see Claude Code on Amazon Bedrock. The gateway-side upstream:
An empty auth block uses the AWS SDK’s default credential chain: env vars, ~/.aws/credentials, ECS task role, EC2 instance metadata, or IRSA on EKS. In production, give the gateway pod an IAM role instead of embedding static keys in a container image. Explicit credentials must be complete: the gateway fails at boot when aws_access_key_id and aws_secret_access_key aren’t set together, or when aws_session_token is set without them. Before v2.1.207, a partial auth: block passed validation.

Claude Platform on AWS

Claude Platform on AWS serves the first-party Anthropic API on AWS infrastructure at aws-external-anthropic.<region>.api.aws. It uses first-party model IDs, honors anthropic-beta headers as sent, and serves count_tokens, so none of the Bedrock-specific translation applies. The anthropicAws provider requires Claude Code v2.1.198 or later; earlier gateway releases reject it at boot. For the client-side deployment of the same platform, see Claude Code on Claude Platform on AWS. The gateway-side upstream:
The platform runs in a separate AWS account from Amazon Bedrock and signs SigV4 requests for its own service name, aws-external-anthropic, so a Bedrock-scoped IAM role doesn’t authorize it. An API key in auth.api_key takes precedence when SigV4 credentials are also set. An empty auth block uses the AWS SDK’s default credential chain, the same chain the Amazon Bedrock upstream uses. Because the platform resolves first-party model IDs, the built-in catalog routes to it with no models: block. When you curate a models: list, key the entry anthropicAws: with the first-party ID.

Google Cloud Agent Platform

For the equivalent client-side setup, see Claude Code on Google Cloud. The gateway-side upstream:
An empty auth block uses Application Default Credentials: GOOGLE_APPLICATION_CREDENTIALS, GCE metadata, or GKE Workload Identity. Service-account JSON key files are supported but discouraged; use Workload Identity or attach a service account to the GCE or Cloud Run instance. Set region: global to use the global endpoint for Google Cloud’s Agent Platform instead of a regional one. Google then routes each request to an available region, so you don’t track per-region model availability. Setting a specific region pins every request to it.

Microsoft Foundry

For the client-side Microsoft Foundry deployment, see Claude Code on Microsoft Foundry. The gateway-side upstream:
use_azure_ad: true resolves through DefaultAzureCredential: Managed Identity on AKS, ACI, or App Service; the Azure CLI; or environment credentials. API keys work but are project-wide and don’t rotate automatically. Microsoft Foundry’s endpoint is derived from resource:; set the optional base_url to override it for sovereign clouds such as Azure Government.

Multiple upstreams

The same provider can appear more than once with a distinct name:. This covers different regions, different accounts via different credential chains, provisioned throughput versus on-demand, and cross-provider fallback. The gateway tries upstreams in order. 5xx, 429, 401, 403, 404, timeouts, and missing-endpoint (501) fail over; other 4xx doesn’t. 429 is per-upstream capacity, so provisioned-throughput (PT) exhaustion fails over to on-demand. 404 is per-upstream model availability, so an upstream that hasn’t enabled a model doesn’t block a later upstream that serves it. An upstream that can’t resolve the requested model is skipped without a network round-trip. This example routes a provisioned-throughput Amazon Bedrock allotment first, overflows to on-demand and a second account, and falls back to the Anthropic API last:
Failing over between cloud providers, or to the direct Anthropic API, changes which agreement, geography, and other terms govern the request. The CLI applies the same feature gating to gateways regardless of which upstream serves a given request, so failover doesn’t send a body field an upstream would reject.

Optional sections

admin

Optional. Enables /v1/organizations/spend_limits, which mirrors Anthropic’s public Admin API, and per-developer spend enforcement on /v1/messages. See Spend limits for how caps are set and enforced; this section covers the gateway.yaml keys that turn the feature on and tune it.

enforcement

The enforcement block controls how spend-limit checks behave when the store is unavailable.

models

The models block is an optional admin-curated model list, served at /v1/models and used to translate model IDs per upstream. It is required for non-US Amazon Bedrock regions, Amazon Bedrock provisioned-throughput ARNs, and Microsoft Foundry deployment names.

managed

The managed block defines role-based access policies keyed on IdP groups or email domain. Policies are evaluated in order; the first match is selected, then merged onto the match: {} catch-all base described below. They are served per-user at GET /managed/settings with ETag/304 caching.
A match: {} catch-all, conventionally listed last, is treated as a base layer. Every other policy inherits any key it doesn’t set from the catch-all, so per-role entries only need to list what differs from the org default. The merge rules depend on the key type:
  • Allow-lists: availableModels and permissions.allow. A specific policy’s list fully replaces the base’s.
  • Deny-lists and hook arrays: permissions.deny, permissions.ask, disabledMcpjsonServers, deniedMcpServers, blockedMarketplaces, and every hooks event-type array. These take the union of base and policy, so an org-wide deny or audit hook can’t be accidentally dropped by a per-role override.
  • Record-typed keys: env, modelOverrides, and skillOverrides. These shallow-merge, so a per-role env block overrides keys it sets and inherits the rest from the base.
availableModels is also enforced server-side at /v1/messages, so a denied model returns 400 regardless of what the client sends. An authenticated user who matches no policy gets the gateway’s defaults, which means every model in the catalog and no managed settings. Add a match: {} catch-all last if you want a guaranteed default policy.
The gateway keeps no user directory of its own. It authorizes each request from the user’s IdP token, reading group membership from the token’s groups claim and evaluating policies against it. There is no roster to enumerate and no accounts to pre-create, and therefore no SCIM endpoint, because there is nothing for SCIM to sync into.Run user and group lifecycle management at the source of truth, which is your IdP’s native SCIM provisioning or a dedicated identity-governance platform. Membership and deprovisioning governed there flow into the gateway automatically through the token. If you want SCIM provisioning of Claude accounts themselves, that is a Claude for Enterprise capability.Two propagation clocks apply:
  • Policy contents: editing a policy and redeploying reaches connected clients on their next managed-settings poll, within an hour
  • Group membership: changing a user’s group membership changes which policy matches them. This takes effect on the next session re-mint, meaning the next silent refresh, bounded by session.ttl_hours.

What goes in cli

Each cli value is a complete Claude Code managed-settings.json document, the same schema you would deploy via MDM or /etc/claude-code/managed-settings.json, expressed here as YAML. The CLI applies the delivered document at the managed tier, above user and project settings. The gateway validates each document against the CLI’s settings schema at boot, so an unrecognized top-level key or a recognized key with a malformed value fails boot with an error naming every offending key. Deliberately open parts of the schema still accept arbitrary values, because newer clients may recognize entries the gateway’s schema doesn’t. These open keys are env, pluginConfigs, and keys nested under permissions. Because validation uses the schema bundled with the gateway’s installed version, putting a top-level settings key introduced by a newer Claude Code release into managed config requires upgrading the gateway first. Smoke-test a new policy on one client before rolling it out. The full key reference is in Claude Code settings. The keys most operators reach for first:
Because these settings arrive over the network, the CLI shows each developer a one-time security approval dialog before applying anything that can run a shell command or alter where traffic goes. The dialog covers:
  • hooks
  • env variables that aren’t on the CLI’s built-in safe list
  • shell-execution settings such as apiKeyHelper and statusLine
  • managed CLAUDE.md content
The safe list determines which env variables apply without approval:
  • On the safe list: auto-update and model-name vars
  • Not on the safe list: proxy vars, base-URL vars, and OTEL_EXPORTER_OTLP_ENDPOINT
The gateway’s telemetry configuration pushes OTEL_EXPORTER_OTLP_ENDPOINT, so setting telemetry.forward_to triggers the dialog on each interactive client. The dialog protects the developer’s machine from a compromised or hostile gateway, not the organization from the developer. A non-interactive run with the -p flag can’t show the dialog. It applies the pushed settings for that run only and doesn’t record them as approved, so the developer’s next interactive session still shows the dialog. Before v2.1.207, a non-interactive run saved the settings as approved and no later interactive session showed the dialog for them. If a developer declines, Claude Code exits rather than applying the policy. Pushing a new hook or non-safe env var to a broad policy therefore means an approval prompt on every matching developer’s next startup. The cli key was named settings in earlier releases. That spelling is still accepted as an alias, but new deployments should use cli.

Precedence with other managed sources

If a device also has a local managed-settings.json or MDM-delivered policy, the managed sources don’t merge. The highest-priority source provides all policy settings, ranked in this order with highest priority first:
  1. The policy helper
  2. Gateway-delivered settings
  3. MDM, via the HKLM registry on Windows or a plist on macOS
  4. The managed-settings.json file
  5. The HKCU registry, on Windows only
Embedding hosts can supply policy through the SDK managedSettings option. Whether it applies depends on the machine’s managed configuration:
  • On machines with an admin-deployed managed source, it is ignored unless the highest-priority source opts in with parentSettingsBehavior: "merge".
  • It is never merged while a policyHelper is configured.
  • When merged, it passes through a restrictive-only allowlist. Restrict parent settings lists which allow-direction settings still apply without the allowManaged*Only locks.
The following keys are honored when any admin source above the user-writable HKCU tier sets them, regardless of which source provides the rest of the policy. When a policyHelper is configured, its output is the only source these checks read:
  • sandbox.network.allowManagedDomainsOnly and sandbox.filesystem.allowManagedReadPathsOnly: when locked, the corresponding allowlists are unioned across sources
  • allowAllClaudeAiMcps: allow-only override for the claude.ai MCP server allowlist
  • sandbox.bwrapPath and sandbox.socatPath: filesystem paths to the sandbox helper binaries
  • forceRemoteSettingsRefresh: blocks startup until remote managed settings are freshly fetched, so an MDM or file policy that sets it is honored even when a cached remote payload that lacks the key is the highest-priority source
Every other key, including disableBypassPermissionsMode, comes from the highest-priority source only. Two parent-settings checks read every admin source:
  • When any admin source sets allowManagedPermissionRulesOnly, Claude Code drops parent-supplied permission allow rules and additionalDirectories. The key’s effect on the developer’s own rules still follows the highest-priority source.
  • A forceLoginOrgUUID or allowedMcpServers value in any admin source blocks a parent-supplied one. The value that applies still comes from the highest-priority source.
See Settings precedence for the same rules on the settings page. Gateway policies apply to every Claude Code invocation on the machine, including non-interactive claude -p runs and sessions spawned by the Agent SDK. If the gateway is unreachable at startup, signed-in sessions exit with an error rather than running without their policy.
mcpServers inside a policy’s cli block is rejected at gateway boot. Per-group MCP distribution is not available; deploy MCP servers via the file-based managed-mcp.json on each device or let developers add them locally.

telemetry

The CLI sends OpenTelemetry Protocol (OTLP) over HTTP metrics, logs, and, when enabled, traces to the gateway, which relays them verbatim to each configured destination. See Monitoring usage for the metrics and events the CLI emits. The CLI stamps each export with the authenticated user’s identity, read from the gateway-issued JWT: the user.id, user.email, and user.groups attributes. Per-developer cost and usage attribution therefore works with no developer-side configuration.
Each destination opts into metrics, logs, and traces independently, and the default is metrics only. The signals differ in sensitivity:
  • Metrics: aggregate counters such as token counts, request counts, and latency
  • Logs and traces: can carry full bash commands, tool inputs, and file paths, covering anything Claude Code does on a developer’s machine
Enable logs and traces only on destinations with the access controls and retention policy that data warrants.
Telemetry is off in the CLI by default. Configuring telemetry.forward_to together with listen.public_url turns it on. The gateway pushes five env vars to every connected client through /managed/settings:
  • CLAUDE_CODE_ENABLE_TELEMETRY=1
  • OTEL_METRICS_EXPORTER=otlp
  • OTEL_LOGS_EXPORTER=otlp
  • OTEL_TRACES_EXPORTER=otlp
  • OTEL_EXPORTER_OTLP_ENDPOINT=<public_url>
The pushed endpoint is built from the public URL, so metrics and logs need no OTEL configuration from developers or policies. The pushed configuration is applied at the managed tier, overriding OTEL_* variables a developer sets locally. Traces additionally require CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 on each client. The gateway doesn’t push that variable, so set it through a managed policy’s env block. It isn’t on the CLI’s safe list, so delivering it through a policy is covered by the same security approval dialog that the pushed OTLP endpoint already triggers. Both protobuf and JSON OTLP encodings are relayed, and any OpenTelemetry-compatible backend works as a destination.

HTTP tuning

Four optional top-level blocks, access_control, limits, timeouts, and rate_limits, tune the HTTP surface. The defaults suit most deployments.

Complete example

This full reference config exercises every core section; the HTTP tuning blocks keep their defaults. Copy it, delete what you don’t need, and fill in your values. The config in the Quickstart is a minimal version of this.
gateway.yaml

Client-side managed settings

Everything above configures the gateway server. Pointing developer machines at it is configured separately, on each device, through Claude Code’s managed settings. The gateway can’t push the login keys itself, because they’re what tell the client where the gateway is. For the CLI, set these keys in the per-OS managed-settings.json. The two login keys route each developer’s /login to your gateway:
parentSettingsBehavior: "merge" keeps Claude Desktop’s policy delivery to its embedded Claude Code sessions working; Deliver policy to Claude Desktop sessions explains the mechanism and where the opt-in must sit. Deploy the managed-settings.json file to each device, typically via your MDM platform. The file path differs by platform: A registry policy on Windows or a managed-preferences plist on macOS replaces the managed-settings.json file rather than merging with it, apart from the exception keys and cross-source checks above. All three keys in this snippet follow the highest-priority-source rule, so fleets that deliver policy through Group Policy or configuration profiles must put all three in that mechanism instead. forceLoginGatewayUrl, and the "gateway" value of forceLoginMethod, are honored only from the admin-controlled managed tier. A developer setting them in their own ~/.claude/settings.json has no effect.