> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/agentgateway/agentgateway/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> Configure Agentgateway with YAML or JSON files. Covers static config, local file-watch config, and XDS dynamic config.

Agentgateway is configured through YAML or JSON files (the two formats are fully equivalent). Configuration is split into three forms: **static config** for global process settings, **local config** for full proxy configuration with hot-reload, and **XDS config** for remote control plane management.

## File format

Configuration files use the following top-level structure:

```yaml theme={null}
# yaml-language-server: $schema=https://agentgateway.dev/schema/config

# Global process settings (static — requires restart to change)
config:
  logging:
    format: json
  adminAddr: "0.0.0.0:9901"

# Traffic configuration (dynamic — hot-reloaded on file change)
binds:
- port: 3000
  listeners:
  - routes:
    - backends:
      - host: upstream:8080
```

<Note>
  Adding the `# yaml-language-server` comment at the top of your config file enables schema validation and autocompletion in editors that support the YAML Language Server.
</Note>

JSON is fully equivalent to YAML:

<CodeGroup>
  ```yaml config.yaml theme={null}
  binds:
  - port: 3000
    listeners:
    - routes:
      - backends:
        - host: upstream:8080
  ```

  ```json config.json theme={null}
  {
    "binds": [
      {
        "port": 3000,
        "listeners": [
          {
            "routes": [
              {
                "backends": [
                  { "host": "upstream:8080" }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
  ```
</CodeGroup>

## Static configuration

The `config` block contains settings that are read once at startup. These control global behaviors that apply to the entire process.

```yaml theme={null}
config:
  # Logging
  logging:
    format: json          # "json" or "text"
    level: info           # trace, debug, info, warn, error

  # Observability servers
  adminAddr: "0.0.0.0:9901"      # Admin UI
  statsAddr: "0.0.0.0:9902"      # Prometheus metrics
  readinessAddr: "0.0.0.0:9903"  # Readiness probe

  # DNS resolver
  dns:
    lookupFamily: Auto    # All, Auto, V4Preferred, V4Only, V6Only

  # OpenTelemetry tracing
  tracing:
    otlpEndpoint: "http://otel-collector:4317"
    randomSampling: "0.1"  # 10% sampling (CEL expression)

  # Backend connection pool
  backend:
    connectTimeout: 10s
    poolIdleTimeout: 90s
    poolMaxSize: 100

  # Session management
  session:
    key: "<openssl rand -hex 32>"  # AES-256-GCM key for session tokens
```

<Warning>
  Static configuration changes require a process restart. Use local config or XDS for anything that needs to change at runtime.
</Warning>

### Static config reference

| Field                           | Description                                          |
| ------------------------------- | ---------------------------------------------------- |
| `config.logging.format`         | Log format: `json` or `text`                         |
| `config.logging.level`          | Log level: `trace`, `debug`, `info`, `warn`, `error` |
| `config.adminAddr`              | Admin UI address (`ip:port`)                         |
| `config.statsAddr`              | Prometheus metrics address (`ip:port`)               |
| `config.readinessAddr`          | Readiness probe address (`ip:port`)                  |
| `config.dns.lookupFamily`       | IP family preference for DNS lookups                 |
| `config.tracing.otlpEndpoint`   | OTLP endpoint for OpenTelemetry traces               |
| `config.tracing.randomSampling` | CEL expression evaluating to 0.0–1.0 or bool         |
| `config.backend.connectTimeout` | Upstream connection timeout                          |
| `config.backend.poolMaxSize`    | Max idle connections per host                        |
| `config.session.key`            | AES-256-GCM key for encrypted session tokens         |
| `config.workerThreads`          | Number of async worker threads                       |
| `config.enableIpv6`             | Enable IPv6 support                                  |

## Local configuration

The dynamic part of the configuration — binds, listeners, routes, backends, and policies — lives outside the `config` block. When you run Agentgateway with a config file, it watches the file for changes and **hot-reloads** the configuration without restarting.

### File watch reloads

<Steps>
  <Step title="Initial load">
    At startup, Agentgateway reads the config file and translates it into the Internal Representation (IR). The proxy begins handling traffic.
  </Step>

  <Step title="File change detected">
    When the file is modified (saved), the file watcher detects the change and re-reads the file.
  </Step>

  <Step title="Validation and translation">
    The new configuration is validated and translated into a new IR. If validation fails, the old configuration stays in place and an error is logged.
  </Step>

  <Step title="Atomic swap">
    The new IR atomically replaces the old one. In-flight requests complete against the old configuration. New requests use the new configuration.
  </Step>
</Steps>

<Tip>
  You can also point `config.localXdsPath` to a separate file that contains only the dynamic configuration (binds, listeners, routes, backends). This keeps static and dynamic config cleanly separated.
</Tip>

## Configuration hierarchy

The dynamic configuration follows a strict hierarchy: `binds` → `listeners` → `routes` → `backends`, with `policies` attachable at the route level.

### Binds

A **Bind** defines a TCP port to listen on.

```yaml theme={null}
binds:
- port: 3000        # Required: TCP port number
  listeners: [...]  # One or more listeners on this port
```

### Listeners

A **Listener** defines how connections on a port are handled. Multiple listeners on the same port are distinguished by `hostname`.

```yaml theme={null}
binds:
- port: 3000
  listeners:
  - name: my-listener
    hostname: api.example.com    # Optional; supports wildcards
    protocol: http               # http (default)
    tls:
      cert: /certs/tls.crt
      key: /certs/tls.key
      minTLSVersion: "1.2"
    routes: [...]
```

| Field               | Description                                                |
| ------------------- | ---------------------------------------------------------- |
| `name`              | Optional listener name                                     |
| `hostname`          | Hostname to match; supports wildcards like `*.example.com` |
| `protocol`          | Protocol: `http`                                           |
| `tls.cert`          | Path to TLS certificate                                    |
| `tls.key`           | Path to TLS private key                                    |
| `tls.minTLSVersion` | Minimum TLS version (`1.2` or `1.3`)                       |
| `tls.maxTLSVersion` | Maximum TLS version (`1.2` or `1.3`)                       |
| `tls.cipherSuites`  | Optional cipher suite allowlist (order preserved)          |

### Routes

A **Route** matches incoming requests and applies policies before forwarding to backends.

```yaml theme={null}
routes:
- name: my-route
  hostnames:
  - api.example.com
  matches:
  - path:
      pathPrefix: /v1/
    method: GET
    headers:
    - name: x-api-version
      value:
        exact: "2"
  policies:
    cors:
      allowOrigins: ["*"]
    auth:
      jwt:
        issuer: https://auth.example.com
  backends:
  - host: api-service:8080
```

**Match types:**

| Match type | Fields                         | Description              |
| ---------- | ------------------------------ | ------------------------ |
| Path       | `exact`, `pathPrefix`, `regex` | Match by request path    |
| Header     | `name` + `exact` or `regex`    | Match by header value    |
| Method     | `method`                       | Match by HTTP method     |
| Query      | `name` + `exact` or `regex`    | Match by query parameter |

### Backends

A **Backend** defines an upstream target. The type is inferred from the fields you provide.

<Tabs>
  <Tab title="HTTP backend">
    ```yaml theme={null}
    backends:
    - host: api-service:8080
    ```
  </Tab>

  <Tab title="MCP backend">
    ```yaml theme={null}
    backends:
    - mcp:
        targets:
        - name: everything
          stdio:
            cmd: npx
            args: ["@modelcontextprotocol/server-everything"]
    ```
  </Tab>

  <Tab title="A2A backend">
    ```yaml theme={null}
    backends:
    - host: localhost:9999
    ```

    With the `a2a: {}` policy on the route to mark it as A2A traffic:

    ```yaml theme={null}
    policies:
      a2a: {}
    ```
  </Tab>
</Tabs>

### Policies

Policies are attached at the route level and control authentication, authorization, rate limiting, observability, and traffic shaping.

```yaml theme={null}
routes:
- policies:
    # CORS
    cors:
      allowOrigins: ["*"]
      allowHeaders: ["mcp-protocol-version", "content-type", "cache-control"]
      exposeHeaders: ["Mcp-Session-Id"]

    # JWT authentication
    auth:
      jwt:
        issuer: https://auth.example.com
        jwksUri: https://auth.example.com/.well-known/jwks.json

    # CEL-based authorization
    authz:
      rules:
      - expression: 'jwt.sub == "allowed-user"'

    # Rate limiting
    rateLimit:
      requests: 100
      window: 60s
  backends:
  - host: upstream:8080
```

## Real-world examples

<CodeGroup>
  ```yaml Basic MCP proxy (basic/config.yaml) theme={null}
  # yaml-language-server: $schema=https://agentgateway.dev/schema/config
  binds:
  - port: 3000
    listeners:
    - routes:
      - policies:
          cors:
            allowOrigins:
            - "*"
            allowHeaders:
            - mcp-protocol-version
            - content-type
            - cache-control
            exposeHeaders:
            - "Mcp-Session-Id"
        backends:
        - mcp:
            targets:
            - name: everything
              stdio:
                cmd: npx
                args: ["@modelcontextprotocol/server-everything"]
  ```

  ```yaml A2A proxy (a2a/config.yaml) theme={null}
  config:
    logging:
      format: json
  frontendPolicies:
    accessLog:
      add:
        backend: backend
  binds:
  - port: 3000
    listeners:
    - routes:
      - policies:
          cors:
            allowOrigins:
            - '*'
            allowHeaders:
            - content-type
            - cache-control
          # Mark this route as A2A traffic
          a2a: {}
        backends:
        - host: localhost:9999
  ```
</CodeGroup>

<CardGroup cols={2}>
  <Card title="Architecture" icon="building" href="/concepts/architecture">
    Understand how static config, local config, and XDS map to the proxy's Internal Representation
  </Card>

  <Card title="CEL expressions" icon="code" href="/concepts/cel-expressions">
    Write authorization rules and transformation expressions using CEL
  </Card>
</CardGroup>
