Docs
AISIX AI GatewayOperateDeployment and RuntimeGitOps

GitOps

Drive AISIX configuration from Git — a validate-ship-reload pipeline for the open-source resources.yaml gateway, and an Admin API apply-and-verify pipeline for AISIX Enterprise.

AISIX configuration can be version-controlled in Git and delivered through a CI/CD pipeline, but the pipeline you build depends entirely on which product you run. The open-source AISIX gateway and AISIX Enterprise disagree about where configuration lives, so they disagree about what a pipeline pushes and how it confirms success.

  • Open-source AISIX gateway: configuration is a file the gateway process reads. A pipeline ships that file and reloads the process.
  • AISIX Enterprise: configuration is control-plane state, projected asynchronously to connected gateways. A pipeline calls an API and then polls for projection to finish.

Pick the track that matches your deployment. Running gateways connected to AISIX Enterprise alongside a standalone open-source gateway means running both pipelines, each against its own resources.

Track A: Open-Source AISIX Gateway

This track applies to a gateway with no control plane, configured through a declarative resources.yaml file.

How AISIX Loads Configuration

Three properties of the load model shape everything else in this pipeline:

  • One file, no includes. resources_file names exactly one path. AISIX does not load a directory, and it does not resolve include or import directives. If your source is split into multiple documents, something outside AISIX has to assemble them into one YAML document before it reaches the gateway.
  • Full-snapshot replace. A successful load does not merge with the previously active configuration — it replaces it. A collection or entry missing from the file you ship is gone from the running gateway, not left alone.
  • Explicit reload only. AISIX does not watch resources.yaml for changes. The file is read at process startup and re-read only when the process receives SIGHUP. A file edited on disk with no reload trigger changes nothing.

See Resources File Reference for the complete format and Configuration Propagation for the reload mechanics this pipeline automates.

Repository Layout

Store one complete resources.yaml at the repository root — or wherever your build tooling expects it — matching the file AISIX actually loads:

aisix-config/
├── resources.yaml
└── README.md

A single file is the layout that needs no assembly step and can't drift from what the gateway loads.

If your team prefers reviewing smaller diffs, you can instead keep the source split into one fragment per collection:

aisix-config/
├── fragments/
│   ├── provider_keys.yaml
│   ├── models.yaml
│   ├── api_keys.yaml
│   └── guardrails.yaml
└── resources.yaml   # generated by CI, not hand-edited

Treat fragments/ as a CI-only convenience. AISIX never reads it — only the assembled resources.yaml is a valid load target. Name fragments after real collections from the Resources File Reference (provider_keys, models, api_keys, guardrails, passthrough_routes, and so on); there is no routes collection, so don't structure fragments around one.

CI: Validate

Run aisix validate against the assembled file on every change, before merge:

aisix validate --resources resources.yaml

validate runs the identical load pipeline the gateway uses — parsing, ${VAR} interpolation, name-reference resolution, schema validation, and cross-reference checks — without starting a listener, so a clean exit means the file will load. Run it with the same environment variables the target gateway will have, since ${VAR} references resolve against whatever environment the validate process sees. Treat a non-zero exit as a merge blocker.

CD: Assemble, Ship, Reload

On merge, or on promotion to each environment:

  1. Assemble. If you keep fragments, concatenate them into the complete resources.yaml your target gateway will load. Re-run aisix validate against the assembled result — CI validated a fragment set, not necessarily the exact bytes about to ship.

  2. Ship the file. Deliver it to wherever the gateway's resources_file setting points — the mounted path in a container image, a volume, a config map, or a file synced onto the host.

  3. Trigger a reload. Send SIGHUP to the running process, or perform a rolling restart that boots each replacement instance with the new file already in place. AISIX does not pick the file up on its own.

  4. Confirm. Query the metrics and status listener and check that state is synced and apply_seq advanced:

    curl -sS "http://127.0.0.1:9090/status/config"

    See Confirm the Applied Configuration for the full check, and follow it with a caller-facing request through the endpoint that changed — a synced reload confirms the file loaded, not that the specific resource behaves as intended.

This loop is synchronous end to end: ship, reload, poll /status/config until it reflects the new snapshot, then verify with a live request. There is no separate projection step to wait on.

Rollback

Revert the Git commit and run the same pipeline forward: validate the reverted file, assemble it if you use fragments, ship it, and reload. There is no separate rollback mechanism — a rollback is a normal deployment of an older, known-good file.

One property of the reload path makes this safer than it sounds: if the reverted file is invalid for any reason, a rejected SIGHUP reload does not take the gateway down. AISIX logs the rejection, exposes it through /status/config, and keeps serving the last valid snapshot. The same is not true at a fresh boot — a gateway starting cold with an invalid file stops immediately rather than coming up empty. Validate before every reload, and treat a fresh-boot deployment (a new instance, not a running one receiving SIGHUP) as the case where a bad file has real consequences.

Track B: AISIX Enterprise

This track applies to gateways connected to an AISIX Enterprise control plane, self-hosted or API7-hosted. There is no resources.yaml to ship — configuration is control-plane state, and a pipeline changes that state through the Admin API.

Where Configuration Actually Lives

AISIX Enterprise splits resource ownership between two scopes, and a GitOps pipeline should mirror that split rather than impose a flat structure:

  • Organization-owned resources — provider keys, MCP and OpenAPI-backed servers, A2A agents — belong to the account-level organization and project only to the environments they're explicitly allowed into.
  • Environment-owned resources — models, caller API keys, guardrails and their attachments, cache policies, rate-limit policies, OIDC providers, claim mappings, passthrough routes, observability exporters, and MCP environment access and authentication settings — belong to one environment and project to every gateway connected to it.
  • Team access policies belong to an organization through their team and project to every environment in that organization.

See Resource Projection for the complete ownership-and-projection table and How AISIX Enterprise Composes for the organization/environment scope model this is built on.

Repository Layout

Structure the repository around that same boundary instead of a fictional flat tree — one directory for organization-scoped resources, one per environment for environment-scoped resources:

aisix-config/
├── organization/
│   ├── provider-keys.yaml
│   ├── mcp-servers.yaml
│   └── a2a-agents.yaml
└── environments/
    ├── dev/
    │   ├── models.yaml
    │   ├── caller-api-keys.yaml
    │   └── guardrails.yaml
    ├── staging/
    └── prod/

The files here are desired-state documents your pipeline diffs against the control plane — not a resources.yaml AISIX itself loads. Their shape is up to you; keep it close to the Admin API's own resource kinds so the apply step in CI stays a straightforward diff.

CI: Apply via the Admin API

Apply changes through the AISIX Enterprise Admin API, not a file drop. On merge, or on promotion to each environment, have CI:

  1. Read current state from the Admin API for the affected organization or environment.
  2. Diff it against the desired state stored in Git.
  3. Issue the matching create, update, or delete calls for what changed, authenticated with a write-scoped admin token.

There is no directory or file the control plane loads on its own — every change is a discrete API call, so the pipeline owns the diffing that a file-based reload gets for free.

CD Verification

Publication through the Admin API is asynchronous: a successful write confirms only that the control plane accepted the change, not that any gateway has applied it. This is the structural difference from Track A, where a synced /status/config after a reload is the end of the loop. Here, verification is a separate step your pipeline has to run after the apply call returns. Follow the three-step method from Resource Projection:

  1. Compare revisions. Query dp_nodes for the environment and compare store_revision against each node's applied_revision. A node is current once its applied_revision is equal to or greater than store_revision.
  2. Inspect the gateway snapshot. Query /status/config on the gateway instance handling traffic and confirm state is synced, applied.applied_revision reflects the expected revision, and rejected and partially_compatible are both empty. This listener defaults to 0.0.0.0:9090 and its routes are unauthenticated (see Ports), so it's typically not published outside the deployment. Reach it from your CI runner over a private path instead of exposing it publicly — a container attached to the same Docker network, a kubectl port-forward or in-cluster request on Kubernetes, or an internal load balancer.
  3. Verify caller-visible behavior. Call GET /v1/models with the relevant caller API key to confirm the expected alias appears, then send a real request through the endpoint that changed.

Have CI poll steps 1 and 2 until they agree, on a bounded timeout, before running step 3. A pipeline that only checks the Admin API's write response and declares success is checking acceptance, not projection.

Rollback

Revert the Git commit describing desired state and re-run the same CI apply step. Because the apply step diffs desired state against current state and issues only the matching Admin API calls, it's idempotent — applying the reverted state is not a different operation from applying any other change. There is no "redeploy a gateway" step in this track: gateways don't hold configuration locally to redeploy. They pick up the correction the same way they pick up any change, through projection, so rollback verification is the same three-step check as any other apply.

Infrastructure as Code

AISIX does not currently publish an official Terraform, Pulumi, or Crossplane provider, for either the open-source resources-file format or the Enterprise Admin API. Drive changes directly from CI instead of through an IaC provider — the aisix validate / reload loop for Track A, or direct Admin API calls for Track B. If your organization standardizes on Terraform or a similar tool elsewhere, treat these pipelines as the AISIX-specific exception until an official provider exists.

Best Practices

  • Keep secrets out of the repository. Track A resolves ${VAR} references from the gateway process environment — reference provider and caller credentials by variable name in resources.yaml and inject the values through your deployment tooling, never commit them. Track B resources reference credentials the control plane already holds or that your CI injects when calling the Admin API; don't store plaintext secrets in the desired-state files either way.
  • Use one branch per environment, or a promotion-based workflow, so a change reaches staging before prod and the repository state for each environment reflects what's actually running there.
  • Tag releases so a rollback target is unambiguous, and so "what changed between these two deploys" is a diff between two tags.
  • Record every change. Track B has a real audit trail for this: the AISIX Enterprise control plane logs control-plane state changes separately from gateway request traffic. See Logging and Auditing for what the audit log captures. Track A has no equivalent inside AISIX itself — rely on your Git history and CI logs as the record of what was deployed and when.

Track A

Track B