Docs/Budget Ai/Self Hosting And Configuration

Self-Hosting & Configuration

On ANTS Cloud the gateway is operated for you. This page is for self-hosted deployments, where the gateway runs as its own service next to web and worker. It is a separate, internet-facing service and is off by default.

Requirements

  • An Enterprise self-hosted license, which provides the llm-gateway entitlement.
  • The gatewayAi feature flag enabled for the users who manage Budget AI, or LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES=true. See What you need to turn it on.
  • The same Postgres and Redis as the rest of the platform, the same event storage bucket that ingestion uses, and a running worker. The worker processes gateway traces and runs the budget and key maintenance jobs.
  • The gateway must share SALT and ENCRYPTION_KEY with web. SALT hashes keys; changing it invalidates every issued key. ENCRYPTION_KEY (64 hex characters) opens provider secrets stored in the database.

Enable with Helm

The gateway ships as the gateway subchart of helm/ants-platform.

Turn on the service and set its public address

yaml
gateway: enabled: true global: gateway: publicBaseUrl: "https://<your-gateway-host>/v1"

global.gateway.publicBaseUrl is the externally reachable address callers point their SDKs at, ending in /v1. It is rendered into web as GATEWAY_PUBLIC_BASE_URL and shown in Project Settings → Budget AI and in key snippets. When it is empty, the settings tab reports that the gateway address is not configured and hides the snippets.

Publish it through the ingress

yaml
global: ingress: gateway: enabled: true host: "<your-gateway-host>" tls: enabled: true secretName: "<your-gateway-tls-secret>"

The gateway ingress routes /v1 only, to the gateway service. Its host must match global.gateway.publicBaseUrl. Enabling the ingress without gateway.enabled: true fails the render.

Set the proxy hop count

Set gateway.config.trustedProxyHops to the number of proxies in front of the gateway. See Client addresses behind proxies.

Deploy and turn on serving per project

After the rollout, an administrator turns on Serve gateway traffic for this project in Project Settings → Budget AI for each project that should use it.

Helm values

ValueDefaultPurpose
gateway.enabledfalseDeploy the gateway
gateway.replicaCount2Replicas when autoscaling is off
gateway.image.repository / tagants-gatewayGateway image
gateway.service.port3040API port, published by the Service
gateway.service.adminPort9091Admin port for health and metrics; a container port only, never on the Service
gateway.config.providerTimeoutMs60000Provider connect, headers and body timeout
gateway.config.providerStreamUsage"true"Ask OpenAI-compatible and Azure OpenAI providers for a usage chunk on streams
gateway.config.maxBodyBytes4718592Request body limit
gateway.config.configPollMs1000Configuration version poll interval
gateway.config.configStaleMs5000Fail-closed bound; must exceed configPollMs
gateway.config.configFullReloadMs60000Full configuration reload interval
gateway.config.probeIntervalMs60000Health check tick and default interval
gateway.config.probeTimeoutMs10000Timeout per health check
gateway.config.telemetryConcurrency16Concurrent trace recordings per replica
gateway.config.trustedProxyHops1Proxies in front of the gateway
gateway.config.trustedProxyCidrsemptyExact proxy addresses or CIDRs to trust instead of a hop count
gateway.config.secretsTtlMs300000How long a secret read from AWS Secrets Manager is cached
gateway.config.database.host / redis.hostemptyHosts the init containers wait for
gateway.resources200m / 256Mi requests, 1 CPU / 1Gi limitsContainer resources
gateway.autoscaling.enabledfalseHorizontal Pod Autoscaler, 2 to 6 replicas at 70 % CPU by default
gateway.podDisruptionBudget.enabledtrueKeeps at least one replica during voluntary disruptions
gateway.terminationGracePeriodSeconds120Lets streamed completions finish during a rollout
gateway.serviceAccount.annotationsemptyFor example an IAM role annotation when secrets are in AWS Secrets Manager
gateway.networkPolicy.enabledfalseEgress policy, see Network policy
global.gateway.publicBaseUrlemptyPublic address, see above
global.gateway.secrets.store / region / prefixdb / empty / ants/gatewayProvider secret storage, read by both web and the gateway
global.ingress.gateway.*disabledGateway ingress host, TLS and annotations

The gateway image is built from gateway/Dockerfile. A deployment that leaves gateway.enabled: false does not need it.

Environment variables

The gateway validates its configuration at start-up and refuses to start when it is incomplete.

VariableDefaultPurpose
PORT / HOSTNAME3040 / 0.0.0.0API listener
GATEWAY_ADMIN_PORT9091Admin listener for /health, /ready and /metrics
SALTrequiredKey hashing; must match web
ENCRYPTION_KEYrequired, 64 hex charactersOpens database-stored provider secrets; must match web
DATABASE_URL, Redis and event storage settingsas for workerConfiguration loading, admission and trace recording
NEXT_PUBLIC_LANGFUSE_CLOUD_REGION, LANGFUSE_EE_LICENSE_KEYunsetPlan resolution for the llm-gateway entitlement
GATEWAY_PROVIDER_TIMEOUT_MS60000Provider timeout
GATEWAY_PROVIDER_STREAM_USAGEtrueRequest usage chunks on OpenAI-compatible and Azure OpenAI streams
GATEWAY_MAX_BODY_BYTES4718592Request body limit
GATEWAY_TRUSTED_PROXY_HOPS1Proxies in front of the gateway
GATEWAY_TRUSTED_PROXY_CIDRSunsetProxy addresses or CIDRs to trust; takes precedence over the hop count
GATEWAY_CONFIG_POLL_MS1000Version poll interval
GATEWAY_CONFIG_STALE_MS5000Fail-closed bound; must be longer than the poll interval
GATEWAY_CONFIG_FULL_RELOAD_MS60000Full reload interval
GATEWAY_SECRETS_STOREdbdb or aws; must match web
GATEWAY_SECRETS_REGIONunsetAWS region for Secrets Manager
GATEWAY_SECRETS_PREFIXants/gatewaySecret path prefix; must match web
GATEWAY_SECRETS_TTL_MS300000Cache lifetime for managed secrets
GATEWAY_PROBE_INTERVAL_MS60000Health check tick and default interval
GATEWAY_PROBE_TIMEOUT_MS10000Timeout per health check
GATEWAY_PROBE_CONCURRENCY8Health checks in flight per replica, up to 64
GATEWAY_TELEMETRY_CONCURRENCY16Concurrent trace recordings
GATEWAY_TELEMETRY_MAX_QUEUE10000Trace recordings that may wait
BUILD_IDunsetReported on traces as gateway.version
OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_SERVICE_NAMEhttp://localhost:4318 / gatewayOperational tracing of the gateway itself

On web, set GATEWAY_PUBLIC_BASE_URL (through global.gateway.publicBaseUrl) and the same GATEWAY_SECRETS_* values.

⚠

The gateway accepts only ANTS-issued keys and provider credentials registered in a project. It refuses to start while any of GATEWAY_DEV_KEY, GATEWAY_DEV_PROJECT_ID, GATEWAY_PROVIDER_BASE_URL or GATEWAY_PROVIDER_API_KEY is set.

Provider secret storage

By default, provider secrets are encrypted into the platform database. To store new credentials in AWS Secrets Manager instead:

yaml
global: gateway: secrets: store: "aws" region: "<aws-region>" prefix: "ants/gateway" gateway: serviceAccount: annotations: eks.amazonaws.com/role-arn: "<role-arn-scoped-to-the-prefix>"

Both web (which writes secrets) and the gateway (which reads them) must see the same store, region and prefix. Scope the IAM policy to the prefix and grant GetSecretValue, PutSecretValue, CreateSecret, DeleteSecret and RestoreSecret; without the last two, deleting a credential or moving it back out of the managed store leaves the secret behind. Existing database credentials are moved one at a time with Move in Models & Routing. See Where provider secrets are stored.

Client addresses behind proxies

Per-key allowed source networks are checked against the caller's address, which the gateway reads from X-Forwarded-For counting proxies from the right. Set trustedProxyHops to exactly the number of proxies in your path: for example 1 for a load balancer straight to the service, 2 for a load balancer then an ingress controller, 0 when connections reach the pod directly.

  • Too high, and a caller can forge its own address and bypass network allow-lists.
  • Too low, and the gateway sees a proxy's address, so every network-confined key is refused.

trustedProxyCidrs lists the proxies exactly instead and takes precedence when set.

Ports, health and metrics

The gateway listens twice:

PortServesExposure
API (3040)/v1/*, /health, /readyPublished by the Service and the gateway ingress (/v1 only)
Admin (9091)/health, /ready, /metricsContainer port only. Do not publish it.

/metrics reports per-project and per-key counts, so it is kept off the public port. It returns JSON with request counts by status, refusal and error counters by code, in-flight requests, gateway latency before the provider call (percentiles), attempt, retry, fallback and benching counts, budget exhaustion counts, health check results, telemetry recorded and failed counts, configuration statistics (projects, keys, last successful version read, pending projects) and process memory and event loop delay.

The chart's liveness probe uses /health and the readiness probe uses /ready, both on the admin port. /ready fails while the replica is draining or its configuration is stale, so the load balancer stops sending it traffic.

On SIGTERM the gateway marks itself not ready, lets in-flight requests and streams finish, and waits for pending trace recordings before exiting, within a shutdown deadline.

Configuration propagation and fail-closed behaviour

The request path never reads Postgres. Each replica holds an in-memory snapshot of every served project's keys, model groups, deployments, credentials and budgets.

  • Every change in the product writes Postgres and its audit log entry in one transaction, then bumps the project's configuration version in Redis.
  • Each replica polls the versions every second (GATEWAY_CONFIG_POLL_MS) and reloads changed projects. A blocked key, a removed grant or a disabled project stops working on every replica within about a second.
  • Every 60 seconds (GATEWAY_CONFIG_FULL_RELOAD_MS) each replica reloads every enabled project. This picks up entitlement changes and any version bump that failed; the product says "Saved, but not yet in force" in that case.
  • Fail closed: if no version read has succeeded within 5 seconds (GATEWAY_CONFIG_STALE_MS), or a project's pending change has not loaded within that time, requests for that project get 503 gateway_config_stale. When the version read itself is failing, every request is refused and /ready fails.

Dependencies

DependencyUsed forIf it is unavailable
RedisConfiguration versions, rate limit and budget counters, reservations, benching, deployment status, response cache, health check schedulingRequests are refused with 503 gateway_config_stale; nothing reaches a provider
PostgresLoading project snapshots, recording threshold crossings and health check resultsReplicas keep serving from their last snapshot until it is older than the stale bound for a changed project
Event storage and workerTurning gateway records into traces, and the budget flush and key maintenance jobsRequests are still served; traces and durable spend catch up when they recover
AWS Secrets Manager (optional)Provider secrets in the managed storeReplicas keep the secrets they already hold

The gateway does not connect to ClickHouse.

The worker runs two Budget AI jobs: one every 10 seconds writes budget and org unit spend from Redis to Postgres, and one every minute blocks keys whose rotation grace window has ended.

Network policy

With gateway.networkPolicy.enabled: true, the gateway's egress is limited to DNS, the platform data namespace and the CIDRs in gateway.networkPolicy.allowedEgressCidrs on the ports in allowedEgressPorts (default 443). A compromised gateway holds provider secrets in memory, so this limits what it can reach. Before enabling it, list every provider endpoint, and when secrets are in AWS Secrets Manager the Secrets Manager and STS endpoints. A Postgres or Redis hosted outside the cluster must also be listed, with its port.

Scaling

Replicas are stateless; all shared state is in Redis. Scale with replicaCount or the autoscaler. Rate limits, budgets, reservations and benching are shared across replicas. The Lowest latency and Least busy strategies use measurements taken on each replica. Keep terminationGracePeriodSeconds long enough for your longest streamed completions.

© 2026 ANTS Platform, Inc.Docs v1.0 · Last updated June 2026