gateway.yaml file the gateway reads at boot, see the Configuration reference.
A production deployment follows four steps in order, and the sections below match them. The first two are where you make choices; the second two are reference material to consult once it’s running.
- Set up your identity provider: register the OAuth client and check the per-IdP notes for Okta, Entra, and Google
- Deploy the gateway: build a pinned container image and run it on Kubernetes, Cloud Run, or your own platform. This section also covers cost, bypass, multiple-gateway, and serverless decisions
- Set up operations: logs, health probes, outage behavior, secret rotation, and upgrades. Reference for when you’re setting up monitoring and runbooks
- Review the security posture: what data flows where, the threat model, and compliance answers. Reference for a security review
Deploy on your private network. Claude Code only connects to a gateway whose address is private. This is a security guard, because a trusted gateway can push settings that run commands on developer machines. Put the gateway you deploy behind an internal load balancer or VPN and give it a hostname that resolves to private IPs only.
Identity provider setup
Register a confidential OAuth/OpenID Connect (OIDC) web application with a single redirect URI,https://<gateway>/oauth/callback, and assign it to the users or groups who should have gateway access.
Any OIDC-compliant IdP works: Okta, Microsoft Entra ID, Google Workspace, Keycloak, Dex, PingFederate, and others. The IdP must meet three requirements:
- Serves
/.well-known/openid-configuration, over HTTPS in production; the gateway accepts anhttp://issuer, and a loopback issuer additionally requiresCLAUDE_GATEWAY_ALLOW_LOOPBACK=1 - Supports the authorization-code flow. PKCE (Proof Key for Code Exchange) is on by default; disable it with
oidc.use_pkce: falsefor IdPs that don’t support it - Returns
emailand optionallygroupsin the id_token, or serves them from the userinfo endpoint withoidc.userinfo_fallback: true
oidc.ca_cert_pem.
A few providers handle email and group claims differently:
- Okta: the org authorization server at
https://example.okta.comreturns a thin id_token that omitsemailandgroups, so setoidc.userinfo_fallback: truewhenever you use it asissuer. A custom authorization server such ashttps://example.okta.com/oauth2/defaultthat includesemailand optionallygroupsin the id_token emits them directly and needs no fallback. Okta emitsgroupsonly when thegroupsscope is requested inoidc.scopesand the app’s groups claim filter allows it;userinfo_fallbackcan’t fill a claim the IdP wasn’t asked for. - Microsoft Entra ID:
issuer=https://login.microsoftonline.com/<tenant-id>/v2.0. Entra emits group Object IDs rather than names, so use the GUIDs inmanaged.policies.match.groups, or use App Roles for human-readable names. If your tenant emits roles underrolesinstead ofgroups, setoidc.groups_claim: roles. - Google Workspace:
issuer=https://accounts.google.com. Google’s id_token doesn’t carry groups. To use group-basedallowed_groupsormanaged.policieswith Google as the IdP, configureoidc.google_groups, which looks up each user’s groups through the Admin SDK Directory API using a service account with domain-wide delegation. Without it, useoidc.allowed_email_domainsfor membership gating andmanaged.policies.match.email_domainfor policy assignment. Google also ignores the standardoffline_accessscope. For refresh tokens, setoidc.scopes: [openid, profile, email]andoidc.extra_auth_params: { access_type: offline, prompt: consent }.
Deployment
The gateway is a single stateless Linux binary that coordinates through Postgres, so deploy it the way you deploy any other stateless service in your environment. Keep it inside your network, where your developers and IdP can reach it over HTTPS, and treat it like any service holding a production credential. A few decisions shape the deployment beyond where it runs:- Cost: no separate license or per-seat fee. The gateway is part of the
claudebinary, so you pay for inference through your existing commitment, plus the compute it runs on. - Bypass: the gateway doesn’t enforce that the only route to a model goes through it. A developer with their own credential can still call the provider directly, so closing that path is a network policy decision, for example blocking egress to
api.anthropic.comexcept from the gateway. Blocking that egress also breaks the WebFetch domain safety check, which callsapi.anthropic.comfrom each developer’s machine. SetskipWebFetchPreflight: truein the managed policy to disable it. - Multiple gateways: each is a separate deployment with its own config, and the CLI stores trust and credentials per gateway hostname, so teams can use different gateways without conflict. To serve multiple OIDC issuers, run separate instances.
- Serverless: Cloud Run works if you set
min-instances: 1to avoid cold OIDC discovery. Lambda and Cloud Functions don’t work, because the gateway is a long-running HTTP server.
listen.trusted_proxies to the proxy’s source ranges so the gateway reads client IPs from X-Forwarded-For. The gateway honors the header only when the TCP peer is trusted. The Google Cloud and AWS worked examples have concrete values per topology. Without trusted proxies, every request appears to come from the proxy’s IP, which collapses per-IP rate limits into one shared bucket and records the proxy’s IP in audit events.
Don’t redirect requests to the gateway’s device-authorization and token endpoints, for example with an HTTP-to-HTTPS or host-canonicalization rewrite at the ingress. Claude Code doesn’t follow redirects on those requests, so an ingress rule that redirects them breaks sign-in and token refresh.
Give the proxy any idle timeout longer than the gateway’s keepalive interval, which depends on the upstream:
- On every upstream except
provider: anthropic, the gateway writes an SSEpingonce a stream has been silent for about 15 seconds. - On
provider: anthropic, the gateway passes the response through unchanged, including the Anthropic API’s own pings.
Container image
Build your own image around the nativeclaude binary from the standard Claude Code release:
- Download the Linux build for your image architecture from a pinned release; see Install a specific version for the download URL.
- Verify it against the release’s GPG-signed
manifest.jsonas described in Binary integrity and code signing. - Copy it into the build context.
- A glibc-based image: the glibc build’s only dynamic dependencies are glibc libraries. Musl-based images need the
linux-x64-muslorlinux-arm64-muslbuild plus additional packages; see Alpine Linux setup. - A writable state directory: the gateway runs as any user, but minimal images have no writable home. Set
CLAUDE_CONFIG_DIRto a writable path such as/tmp/.claude. - The container command:
claude gateway --config /etc/claude/gateway.yaml, with the config file mounted read-only and secrets supplied as environment variables; the gateway listens onlisten.port, default8080.
Kubernetes
Run the gateway as a Deployment, like any stateless service:- Mount the config from a ConfigMap and secrets from a Secret; reference secrets in the YAML via
${file:/path/to/secret}or as environment variables - Terminate TLS at the Ingress and set
listen.public_urlto the Ingress hostname - Point the readiness probe at
GET /readyzand the liveness probe atGET /healthz
upstreams reference has per-platform setup details. For a cross-cloud pairing, such as an Amazon Bedrock upstream on GKE, set explicit credentials in the upstream’s auth block instead.