Deploy Gateways for AISIX Enterprise or AISIX Cloud
The api7/aisix Helm chart deploys, exposes, and scales AISIX gateways in Kubernetes. Gateway listeners, Services, autoscaling, shared Redis, disruption handling, draining, and application settings work the same way whether the gateway receives resources from AISIX Enterprise, AISIX Cloud, or an AISIX OSS resources file. This page begins with the control-plane-connected installation path, then covers those shared Kubernetes operations.
A gateway connected to AISIX Enterprise or AISIX Cloud requires the data-plane manager endpoint and a gateway certificate bundle issued for the target environment. Once connected, it receives models, caller API keys, and policies from the control plane. For AISIX OSS, first deploy the gateway with a resources file, then continue here at Expose the Gateway.
Prerequisites
- A Kubernetes cluster with
kubectlconfigured to access it and Helm 3 installed. - Access to an AISIX Enterprise or AISIX Cloud environment where you can issue a gateway certificate.
- Network access from the cluster to the AISIX Enterprise or AISIX Cloud data-plane manager endpoint. For AISIX Enterprise, complete Install AISIX Enterprise first.
Install the Chart
In the dashboard, open the target environment's Data planes view and issue a gateway certificate. The Kubernetes (Helm) tab provides the data-plane manager endpoint and certificate bundle used below. The example also pins the initial deployment to one replica. The private key is shown only once.
Store the bundle in a Secret so the private key stays out of your values file:
kubectl create namespace aisix-dp
kubectl -n aisix-dp create secret generic aisix-gateway-certificate \
--from-file=cert.pem=./cert.pem \
--from-file=key.pem=./key.pem \
--from-file=ca.pem=./ca.pem
Install the chart against the data-plane manager endpoint from the same view:
helm repo add api7 https://charts.api7.ai
helm repo update
helm install aisix-dp api7/aisix --namespace aisix-dp --version 1.6.0 \
--set controlPlane.baseURL=https://dp-manager.example.com:7944 \
--set controlPlane.certificate.existingSecret=aisix-gateway-certificate \
--set replicaCount=1
The gateway appears in the environment's Data planes view once its first heartbeat lands. The chart defaults to two replicas, but this initial command starts one because rate-limit counters use per-replica memory until you configure shared Redis. The dashboard Helm snippet omits replicaCount, so installing that snippet unchanged starts two replicas. Add --set replicaCount=1 until Redis is configured, as shown above. Share Rate-Limit Counters across Replicas before increasing the replica count or enabling an autoscaler.
Each replica registers as its own instance, and they all share the one certificate.
To see every value the chart accepts:
helm show values api7/aisix --version 1.6.0
The chart source and package are published in the aisix-1.6.0 Helm chart release.
Expose the Gateway
The chart creates a ClusterIP Service for the proxy by default. To publish it through a cloud load balancer:
service:
type: LoadBalancer
port: 80
# Preserve the client source IP, which per-model IP allowlists match on.
externalTrafficPolicy: Local
By default the chart gives the gateway one plain-HTTP proxy listener: it binds containerPorts.proxy, port 3000, inside the container, and the Service publishes it as service.port. A Service can expose port 80 or 443 without making the process bind a privileged container port.
Serve HTTPS and Plain HTTP Together
Set listeners to serve several proxy ports at once, each with its own TLS:
listeners:
- name: https # Port name, shared by the container port and the Service port.
containerPort: 3443
servicePort: 443
# nodePort: 30443 # Optional; only for non-ClusterIP Service types.
tls:
secretName: aisix-proxy-tls
- name: http
containerPort: 3000
servicePort: 80
A non-empty listeners is the complete set of proxy listeners and replaces the single default one. Nothing binds containerPorts.proxy, and service.port and service.nodePort are not read, because each entry carries its own. There is still one proxy Service, and it publishes one port per entry, targeting that entry's container port by name.
Every listener serves the same routes, /livez and /readyz included, so the startup, readiness, and liveness probes target the first entry — over HTTPS when that entry terminates TLS, and the kubelet does not verify the certificate. The chart writes the set to the gateway's configuration file as proxy.listeners. It still writes proxy.addr, which the gateway then ignores and logs an informational line about; no action is needed for that line. This needs a gateway image that supports proxy.listeners.
For the gateway-side rules behind these values, including why a plaintext listener needs a trusted network interface, see Serve HTTPS and HTTP at the Same Time.
The gateway reads TLS material from files, so each TLS listener needs its own kubernetes.io/tls Secret, with the keys tls.crt and tls.key. The chart mounts it read-only at /etc/aisix/tls/<name>. Create it from a certificate and key you already hold:
kubectl -n aisix-dp create secret tls aisix-proxy-tls \
--cert=./tls.crt --key=./tls.key
Or have cert-manager issue and renew it into the same Secret:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: aisix-proxy-tls
namespace: aisix-dp
spec:
secretName: aisix-proxy-tls
dnsNames:
- gateway.example.com
issuerRef:
name: letsencrypt
kind: ClusterIssuer
A rotated certificate reaches the gateway as a changed file in that mount. Roll the pods to pick it up, using the Deployment name from the chart's post-install notes. A release name containing aisix is normally used unchanged; name overrides can change the result:
kubectl rollout restart deployment/<deployment-name> -n <namespace>
Bind a Privileged Container Port
Bind a port below 1024 inside the container only when the gateway must listen on that port directly. The published image runs as non-root UID 10001, and the gateway binary carries the effective CAP_NET_BIND_SERVICE file capability:
containerPorts:
proxy: 80
When listeners is set, put the privileged port on the entry that should bind it, as its containerPort; containerPorts.proxy is not read then.
If you customize the rendered Pod to drop all capabilities, add NET_BIND_SERVICE back to the AISIX container:
spec:
containers:
- name: aisix
securityContext:
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
The Kubernetes Restricted Pod Security Standard allows this capability. Because the binary's file capability has the effective bit set, the container can fail with exec: Operation not permitted when the runtime prevents the capability from being granted.
Use hostNetwork or hostPort only when the gateway must bind directly on a node and the cluster policy allows it. These options introduce node-port conflicts and reduce network isolation; the Baseline and Restricted Pod Security Standards also disallow them.
See Network and Security for the full exposure and credential model.
Run on OpenShift
The chart installs on OpenShift under the default restricted-v2 security context constraint. No custom SCC and no service-account changes are needed.
The chart pins no UID. Its default pod security context is exactly runAsNonRoot: true plus seccompProfile.type: RuntimeDefault, so restricted-v2 supplies the runAsUser and fsGroup from the namespace's allocated range. The gateway image declares a numeric user and runs under any assigned UID with GID 0; the only path written at runtime is an emptyDir volume. The container security context already satisfies restricted-v2 — all capabilities dropped except NET_BIND_SERVICE, a read-only root filesystem, and no privilege escalation.
To pin a fixed UID instead, set the values back:
podSecurityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
For the control plane on OpenShift, see Install on OpenShift.
Run on OKD
Follow OKD for the tested SCC behavior, Routes, platform verification, and upgrade considerations.
Share Rate-Limit Counters across Replicas
Rate-limit counters live in each gateway's own memory by default, so N replicas enforce N times every configured request, token, and concurrency limit. Before running more than one replica — including any replica an autoscaler adds — point every replica at one Redis:
rateLimit:
backend: redis
redis:
url: redis://redis.default.svc:6379
Use rateLimit.redis.existingSecret instead when the connection URL carries a password. To supply the Redis username and password separately from the URL, read them from a Secret through configSecrets, as described in Set Any Other Gateway Configuration. Other settings of the shared Redis connection, such as mode, nodes, or timeout_secs, go under config.ratelimit.redis.
After every replica points to the same Redis deployment, increase replicaCount or enable one of the autoscaling options below. If the deployment does not use request, token, or concurrency limits, you can deliberately accept the per-replica memory backend instead.
Scale on CPU or Memory
autoscaling creates a HorizontalPodAutoscaler for the gateway Deployment:
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 20
targetCPUUtilizationPercentage: 70
The targets are percentages of the pod's resource requests, so the chart sets a CPU request by default. It deliberately sets no CPU limit: throttling adds tail latency to a proxy and suppresses the signal the autoscaler reads. Scaling on CPU requires metrics-server in the cluster.
On Linux, proxy.workers defaults to the CPU parallelism available to the gateway process. A Kubernetes CPU request does not constrain that value, but a CPU limit does. Because the chart sets no CPU limit, set config.proxy.workers explicitly when each replica needs a stable worker count, and keep it within the CPU capacity planned for the replica. See Thread-per-Core Workers for worker configuration and sizing considerations.
When autoscaling is enabled, the Deployment omits spec.replicas so that a later helm upgrade cannot reset the replica count the autoscaler chose. The replicaCount value is ignored from then on.
Pass any behavior policy through unchanged, for example to make scale-down gentler than the Kubernetes default:
autoscaling:
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 60
Use autoscaling.extraMetrics for Pods, Object, or External metrics such as a series exposed through a Prometheus adapter.
Scale on Request Load with KEDA
CPU is a proxy for load. To scale on the gateway's own traffic instead, use KEDA and a Prometheus query over the gateway metrics.
Before enabling the example, prepare these cluster components:
- Install KEDA, including the
ScaledObjectCRD and controller. - Provide a Prometheus server that can query the gateway metrics.
- If the chart should create the scrape configuration shown below, install Prometheus Operator or another controller that consumes
ServiceMonitorobjects. Installing only the CRD lets Helm create the object, but nothing turns it into scrape configuration. Setmetrics.serviceMonitor.labelswhen the Prometheus instance selects ServiceMonitors by label. If Prometheus discovers the metrics Service another way, leavemetrics.serviceMonitor.enabledset tofalseand omit that block.
Confirm the CRDs required by the values you enable:
kubectl get crd scaledobjects.keda.sh
# Required only when metrics.serviceMonitor.enabled is true.
kubectl get crd servicemonitors.monitoring.coreos.com
Configure the chart after those prerequisites are available:
metrics:
serviceMonitor:
enabled: true
keda:
enabled: true
minReplicas: 2
maxReplicas: 20
pollingInterval: 15
cooldownPeriod: 300
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring.svc:9090
query: sum(rate(aisix_llm_requests_total[2m]))
threshold: "100"
aisix_llm_requests_total counts model-inference requests, such as /v1/chat/completions. It does not include MCP or A2A calls. Use aisix_proxy_requests_total if that is the load you want to scale on.
A streamed request is counted when its stream ends, not when it starts. While a burst of long streams is in progress, a rate() over either counter therefore lags behind the incoming load by roughly the length of one stream; steady-state rates are unchanged. Account for that delay when you choose pollingInterval and the query window. A gateway at 1.5.0 or earlier counts a streamed request when its response starts.
autoscaling and keda are mutually exclusive. Enabling both fails the Helm render rather than letting two controllers write spec.replicas.
What Happens during a Scaling Event
A replica the autoscaler adds does not receive traffic before it can serve it. A connected gateway first retrieves configuration from its control plane. Until that configuration is usable, the gateway does not bind the proxy listener, so every probe the chart points at that listener is refused rather than answered. The same wait applies to an open-source AISIX gateway configured to load dynamic resources from etcd.
An open-source AISIX gateway that uses a resources file loads and validates the file synchronously during process startup. With a valid file, its configuration is ready before the proxy listener binds. With an invalid file, the process exits with the validation report. Kubernetes restarts the container because the process failed; it does not wait for the startup-probe budget to expire.
The chart's startupProbe covers the control-plane or etcd wait. Kubernetes holds the readiness and liveness probes back until a startup probe succeeds, so the pod stays out of Service endpoints while it retrieves and applies its initial configuration. The startup probe's budget is periodSeconds x failureThreshold; size it for that configuration-source wait. If the source remains unavailable past the budget, Kubernetes restarts the container. Repeated restarts appear as CrashLoopBackOff. See Startup and the First Configuration for the readiness contract and how to size the budget.
When a replica scales down, Kubernetes starts removing it from Service endpoints and terminating the pod concurrently. Two chart values protect in-flight requests while that happens.
preStopSleepSeconds pauses before the container receives SIGTERM, so endpoint removal reaches every node before the gateway begins shutting down. That covers a balancer watching the Kubernetes API; one that polls a health check instead sees a still-ready pod throughout the pause and is covered by the gateway's own drain window, shutdown.min_drain_secs.
terminationGracePeriodSeconds caps the pause, the drain window, and the in-flight drain that follows. When snapshot persistence is enabled, it also covers up to five more seconds for an outstanding cache write. The gateway drains in-flight requests without a deadline of its own. The Kubernetes default of 30 seconds would cut a streaming response, so the chart ships a longer one.
Both values ship with defaults sized for endpoint withdrawal and long-running requests; helm show values api7/aisix --version 1.6.0 reports the defaults for this release. Raise terminationGracePeriodSeconds for workloads that stream beyond the remaining budget after the pause and drain window. When snapshot persistence is enabled, also leave five seconds for the cache-write drain.
To watch scaling decisions and the metrics behind them:
kubectl -n aisix-dp get hpa aisix-dp --watch
kubectl -n aisix-dp describe hpa aisix-dp
Survive Node Disruption
A PodDisruptionBudget keeps voluntary disruptions — node drains, cluster upgrades — from taking every gateway down at once. Spread constraints keep replicas out of a single failure domain:
podDisruptionBudget:
enabled: true
minAvailable: 50%
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: aisix
The chart uses an emptyDir volume for /var/lib/aisix. It survives a container restart within the same Pod but is lost when the Pod is replaced. A control-plane-connected replacement reconstructs its identity from the certificate Secret and downloads configuration from the control plane; a replacement open-source AISIX gateway mounts and loads its resources file. Keep multiple replicas across failure domains so an existing replica can continue serving during a replacement.
For a gateway connected to AISIX Enterprise or AISIX Cloud to recover cached configuration after a Pod replacement while the control plane is unavailable, customize the workload in two ways. Give each replica its own persistent state directory, and set config.managed.snapshot_cache_enabled to true. Persistent storage alone does not enable the cache. See Restart from Cached Configuration for the full recovery and security requirements.
For the wider deployment pattern used by either AISIX Enterprise or AISIX Cloud, see High Availability.
Set Any Other Gateway Configuration
The chart owns the pod and its startup configuration in both gateway modes. The control plane owns a connected gateway's dynamic resources, while the resources file owns an open-source AISIX gateway's resources. The gateway reads its startup configuration from a file that the chart renders into a ConfigMap and mounts read-only. The config value mirrors that file key for key, with the same section and key names as the Startup Configuration Reference, and lists every setting with the gateway's default. Set only the keys you need to change:
config:
observability:
log_level: debug
upstream:
pool_max_idle_per_host: 32
proxy:
real_ip:
trusted_proxies: ["10.0.0.0/8"]
recursive: true
A key set to null is left out of the file, so the gateway uses its own default. A change to config rolls the pods on helm upgrade. Run helm show values api7/aisix --version 1.6.0 to see every key.
The chart sets some keys from its other values and refuses them under config. The render fails with a message naming the value to use instead:
| Configuration key | Chart value |
|---|---|
proxy.addr, observability.metrics.prometheus.addr | containerPorts |
proxy.listeners, proxy.tls | listeners |
ratelimit.backend, ratelimit.redis.url | rateLimit |
managed.enabled and the control-plane connection under managed | controlPlane |
etcd.endpoints, etcd.prefix | Set from the control-plane connection. |
etcd.env_id, etcd.user, etcd.password_env, etcd.tls | Not configurable through this chart. |
resources_file | standalone |
admin | admin and containerPorts.admin, in standalone mode only. See Enable the Admin API. |
Settings that hold a credential never enter the ConfigMap. Store the value in a Secret and reference it under configSecrets, keyed by the configuration path. The chart hands it to the gateway as an environment variable, which overrides the configuration file:
config:
cache:
backend: redis
redis:
mode: single
configSecrets:
cache.redis.url:
secretName: aisix-cache-redis
key: url
cache.redis.password:
secretName: aisix-cache-redis
key: password
configSecrets accepts cache.redis.url, cache.redis.username, cache.redis.password, ratelimit.redis.username, and ratelimit.redis.password. The chart also refuses these keys under config. The rate-limit Redis URL comes from rateLimit.redis.
Use extraEnvVars only for system-level environment variables, such as TZ, and for variables that a standalone resources file references. An AISIX_* variable set there still overrides the configuration file. See Environment Variables for the naming rules.
Overriding a variable that the chart sets itself still works, and your extraEnvVars value still wins, but only as a compatibility path for existing deployments. For a name that chart 1.5.0 rendered, such as AISIX_MANAGED__CP_BASE_URL or AISIX_MANAGED__HEARTBEAT_INTERVAL_SECS, the chart keeps its own entry ahead of yours, so the container lists the name twice. Helm 4 server-side apply rejects that. On a new installation, set the value through its own chart value or under config instead, and move existing overrides there as well. See Move Application Settings into Chart Values, including why removing such an override takes two upgrades.
Next Steps
Use Prepare AISIX Gateways for Production to review replica count, capacity, observability, and failure-domain planning. For gateway upgrades, follow Upgrade an AISIX Gateway on Kubernetes for values and image checks, rollout verification, and rollback.