A Portkey alternative you run yourself: agentgateway

Disclosure: I work at Solo.io, which builds agentgateway.

agentgateway is an open source Portkey alternative you run yourself: virtual keys, per-key USD or token budgets, guardrails and CEL policy for MCP tools.

agentgateway is an open source gateway you run yourself, and it covers the jobs most teams use Portkey for: virtual keys that hide the provider key, per-key budgets in dollars or tokens, guardrails on prompts and responses, and CEL policy over which MCP tools an agent can see and call. It is Apache 2.0 and lives under the Linux Foundation. Run it as one binary with a YAML file, or on Kubernetes with Gateway API. I ran the virtual key, budget, MCP and SDK examples on this page on v1.6.0 on 2026-10-26, and the outputs below are from those runs. The guardrail section is from the docs.

If you want LiteLLM and Portkey side by side, that is on my comparison page .

What agentgateway is

agentgateway is one data plane for HTTP, gRPC, LLM, MCP and A2A traffic. It is written in Rust, licensed Apache 2.0, was donated to the Linux Foundation in 2025, and became an Agentic AI Foundation (AAIF) project in 2026.

It has two modes, and you choose based on where you run things:

StandaloneKubernetes
InstallA binary , a Docker container, or a single-Deployment Helm chartTwo Helm charts: CRDs plus the control plane
ConfigOne YAML file. The UI can edit itCustom resources plus Gateway API. The UI is read-only
StorageOptional database. Per-key budgets require one (SQLite or PostgreSQL)Token budgets use an external rate-limit service and Redis
Per-key budgetsUSD or tokensTokens (open source). USD needs Solo Enterprise

Every test on this page ran on the open source build. No Solo Enterprise chart, image or license was used.

Concept map

Here is what each Portkey control looks like in agentgateway config.

Portkey conceptagentgateway standaloneagentgateway on Kubernetes
Virtual keyllm.policies.apiKey.keys[], provider key in params.apiKeyConfigMap of hashed keys plus apiKeyAuthentication, provider key in a Secret
Budget limitbudgets[] on a key, unit: USD or unit: TokensrateLimit.global with unit: Tokens
Model accessallowedModels on a keyA CEL authorization rule on the requested model
GuardrailspromptGuard request and response guardspromptGuard on an AgentgatewayPolicy
MCP authorizationmcpAuthorization CEL rules down to the toolCEL rules on an AgentgatewayPolicy , down to the tool
Gateway endpointhttp://localhost:4000/v1Your Gateway’s address

Virtual keys and per-key budgets in standalone mode

This is the whole config I ran. Each client gets its own key. The real provider key lives only in the gateway.

config:
  database:
    url: sqlite://budgets.db
llm:
  port: 4000
  policies:
    apiKey:
      mode: strict
      keys:
      - key: sk-team-a-abc123def456
        metadata:
          name: team-a
        allowedModels:
        - "gpt-4o*"
        budgets:
        - name: daily-spend
          limit:
            unit: USD
            amount: 0.10
          window:
            rolling: 24h
          onBudgetExceeded: Block
      - key: sk-team-b-xyz789uvw012
        metadata:
          name: team-b
        budgets:
        - name: daily-tokens
          limit:
            unit: Tokens
            amount: 250000
          window:
            rolling: 24h
          onBudgetExceeded: Block
  models:
  - name: "*"
    provider: openAI
    params:
      apiKey: "sk-provider-real-key-held-by-gateway"
      # my local mock; drop baseUrl to reach OpenAI
      baseUrl: "http://127.0.0.1:18080/v1"

There is no price table in that file. Since v1.6.0 , a built-in catalog prices common public models. My mock reported 100,000 input and 50,000 output tokens per call on gpt-4o-mini, and the access log priced each call on its own:

gen_ai.request.model=gpt-4o-mini gen_ai.usage.input_tokens=100000 gen_ai.usage.output_tokens=50000 agw.ai.usage.cost.total=0.0450

Calls one to three returned 200. The fourth was blocked before it left the gateway:

HTTP 429
{"error":{"message":"Budget exceeded","type":"rate_limit_error","code":"budget_exceeded"}}

The admin API showed "used":"0.135" against a limit of 0.1. The budget is charged after each response, so the call that crosses the line still completes. Plan for one request of overshoot. The per-key budget docs describe this, plus an Audit action that logs the overage without blocking.

The same key asking for a model outside gpt-4o* got a 403 before any provider call:

HTTP 403
{"error":{"message":"Model is not allowed for this API key","type":"invalid_request_error","code":"model_not_allowed"}}

The provider only ever saw Authorization: Bearer sk-provider-real-key-held-by-gateway. Clients never hold that key. I wrote up the v1.5.0 version of this demo in per-key LLM budgets that return 429 .

Virtual keys and token budgets on Kubernetes

On Kubernetes, I installed the open source agentgateway-crds and agentgateway Helm charts, v1.6.0, with Gateway API v1.6.0, on a local k3s cluster. Virtual keys are stored as hashes in a labeled ConfigMap, and the provider key sits in a Secret:

apiVersion: v1
kind: ConfigMap
metadata:
  name: llm-api-keys
  namespace: agentgateway-system
  labels: {agentgateway.dev/apikey: "true"}
data:
  alice: '{"keyHash": "sha256:84d589f2439e4d9ff4438aa840e63ffcbf231a87e7fef929f975d2bde03dc78c", "metadata": {"user_id": "alice"}}'
  bob: '{"keyHash": "sha256:8373319fb10e2e023343d98ade630529f49c53f567cbc6626d09aaf650b2e58c", "metadata": {"user_id": "bob"}}'
---
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayPolicy
metadata: {name: api-key-auth, namespace: agentgateway-system}
spec:
  targetRefs: [{group: gateway.networking.k8s.io, kind: Gateway, name: agentgateway-proxy}]
  traffic:
    apiKeyAuthentication:
      mode: Strict
      configMapSelector: {matchLabels: {agentgateway.dev/apikey: "true"}}
    rateLimit:
      global:
        domain: agentgateway
        backendRef: {kind: Service, name: ratelimit, namespace: ratelimit, port: 8081}
        descriptors:
        - entries: [{name: user_id, expression: 'apiKey.user_id'}]
          unit: Tokens

The budget itself is a global rate limit counted in tokens, served by an external rate-limit service backed by Redis. I gave each user 100 tokens a day, and the mock reported 19 tokens per call. An unknown key got 401. Alice’s first six calls returned 200, and her seventh and eighth were refused:

HTTP/1.1 429 Too Many Requests
x-ratelimit-limit: 100
x-ratelimit-remaining: 0
retry-after: 26228
content-length: 0

Bob, on his own budget, still got 200. Note the difference from standalone: this 429 has an empty body, and the signal is in the headers. Open source Kubernetes budgets count tokens. Dollar budgets on Kubernetes need Solo Enterprise for agentgateway, which is what my team spend caps with virtual keys demo used.

Guardrails

Guards run on the request and the response. The standalone guardrail docs cover regex guards with built-in detectors and custom patterns, OpenAI Moderation, AWS Bedrock Guardrails, Google Model Armor, and webhooks to your own service. Regex guards can mask, reject or audit. Guards run in sequence, so a cheap regex check can run before a remote one. The same guards exist on Kubernetes .

One change in v1.6.0 matters if you stream: a guard provider that fails while checking a streamed response now rejects the content. Set failureMode: failOpen on the guard if you want the old behavior. My walkthrough is in agentgateway guardrails .

CEL policy for MCP tools

agentgateway authorization is CEL. For MCP, a rule list decides which tools a client can call, and tools that are not allowed are filtered out of tools/list, so the client never sees them. This is the config I ran against the MCP “everything” test server:

mcp:
  port: 3000
  policies:
    mcpAuthorization:
      rules:
      - 'mcp.tool.name == "echo"'
  targets:
  - name: everything
    stdio:
      cmd: npx
      args: ["-y", "@modelcontextprotocol/server-everything"]

Without the rule, tools/list returned 13 tools. With it, the list was ['echo']. Calling echo worked. Calling get-sum returned:

{"jsonrpc":"2.0","id":11,"error":{"code":-32602,"message":"Unknown tool: get-sum"}}

Rules can also match JWT claims, so one tool can be open to everyone and another limited to one user. CEL has sharp edges worth reading about first: CEL authorization gotchas (tested on Solo Enterprise).

For the identity side of who is calling, see SPIFFE identity for AI agents .

Where Portkey’s gateway stands today (from its docs)

Per Portkey’s docs, checked 2026-10-26:

  • The docs index is titled “Portkey (Prisma AIRS AI Gateway) Docs”.
  • The hybrid architecture splits a Data Plane you run from a Management Plane that holds the dashboard, backend, MySQL, ClickHouse, Redis and blob store. The docs say “No prompt or response data is forwarded to the Management Plane directly.” A self-hosted gateway is registered with Prisma AIRS AI Gateway .
  • Budget limits are set per workspace on an integration, with a minimum of $1 or 100 tokens, and reset weekly, monthly or never. Once set, a budget limit “cannot be edited”. Budgets and rate limits can also be set on API keys .
  • The error codes list HTTP 412 for an exhausted budget and 446 for failed guardrail checks.
  • MCP authorization is available at the workspace and MCP server levels. Tool-level authorization is listed as “Coming soon”.
  • The Portkey-AI/gateway repo is MIT licensed and starts with npx @portkey-ai/gateway. Its older docs still link the project at github.com/portkey-ai/rubeus, and that URL now redirects to Portkey-AI/gateway.

Migration checklist

  1. List every Portkey virtual key, its owner, its budget and its allowed models.
  2. Write one apiKey.keys entry per client in standalone, or one hashed ConfigMap entry per user on Kubernetes.
  3. Move provider keys into the gateway config or a Kubernetes Secret. Clients get only virtual keys.
  4. Recreate budgets: unit: USD or unit: Tokens in standalone, unit: Tokens on Kubernetes. Plan for one request of overshoot.
  5. Recreate guardrails and MCP tool rules, then test a blocked request for each.
  6. Point each client at the gateway. For the OpenAI Python SDK, the whole change from a stock client in my test was this:
client = OpenAI(base_url="http://localhost:4000/v1", api_key="<virtual key>")

The call returned 200 with the model’s reply and usage, and the upstream saw only the gateway’s provider key. Test any Portkey-specific headers or SDK features your app uses before cutover.

  1. Watch for 429 budget_exceeded and 403 model_not_allowed in standalone, and for 429 with x-ratelimit-remaining: 0 on Kubernetes. Portkey’s docs list 412 for an exhausted budget, so update any client code that checks for it.

There is no Portkey importer. The agentgateway config importer reads LiteLLM proxy configs only, so a Portkey setup is moved by hand with the concept map above.

Frequently asked questions

Portkey alternative: what is one you can self-host?

agentgateway. I work at Solo.io, which builds it. It is an open source gateway you run yourself, as one binary with a YAML file or on Kubernetes with Gateway API. It covers virtual keys, per-key budgets in dollars or tokens, guardrails and CEL policy for MCP tools. I tested the keys, budgets and MCP rules on v1.6.0.

Portkey alternatives: is agentgateway open source?

Yes. agentgateway is licensed Apache 2.0 and written in Rust, and it is hosted by the Linux Foundation as an Agentic AI Foundation project. Every test on this page ran on the open source v1.6.0 build. Dollar budgets on Kubernetes are the one control on this page that needs Solo Enterprise for agentgateway.

Portkey alternative in United States: does it matter where it runs?

No. agentgateway runs wherever you deploy it, in your own cloud region or data center, whether that is in the United States, Spain or anywhere else. Standalone mode is a binary with a local YAML file and an optional SQLite or PostgreSQL database. It sends prompts only to the model providers you configure, not to a vendor service.

Does agentgateway import a Portkey config?

No. Per the agentgateway docs, checked 2026-10-26, the config importer reads LiteLLM proxy configs only. A Portkey setup moves by hand: recreate providers and virtual keys, then budgets, guardrails and MCP tool rules, using the concept map and the migration checklist on this page. Test one blocked request for each control before you cut over.

Portkey vs LiteLLM: which is better?

This page does not compare the two. My agentgateway, LiteLLM and Portkey comparison covers that question, with each row sourced to the product’s own docs. This page covers agentgateway as a self-hosted Portkey alternative and how each Portkey control maps to its config, with outputs from my own tests.

Does agentgateway support per-key budgets in dollars?

In standalone mode, yes. Since v1.5.0, each API key can carry USD or token budgets, and since v1.6.0 a built-in catalog prices common public models with no extra config. A blocked key gets HTTP 429 with code budget_exceeded. Open source Kubernetes budgets count tokens. Dollar budgets on Kubernetes need Solo Enterprise for agentgateway.

Do I have to change my application code to switch from Portkey?

Starting from a stock OpenAI SDK client, I changed two values: base_url and api_key. The api_key becomes a virtual key that agentgateway swaps for the real provider key. I did not test a client that already sent Portkey headers, so remove and test any Portkey-specific headers or SDK features before you cut over.