This repo (api7/api7-helm-chart) holds the Helm charts for API7 EE components and publishes them to the public Helm repo https://charts.api7.ai (served from the gh-pages branch via a custom domain).
| chart | directory | chart version == EE version? |
|---|---|---|
api7ee3 (Control Plane / Dashboard) |
charts/api7 |
yes — appVersion is the EE version |
gateway (Data Plane) |
charts/gateway |
yes — appVersion is the EE version |
api7-ingress-controller |
charts/ingress-controller |
no — independent product version |
developer-portal-fe |
charts/developer-portal-fe |
no — independent product version |
aisix-cp (AISIX private-deployment control plane) |
charts/aisix-cp |
no — independent product version (source of truth: api7/AISIX-Cloud helm/aisix-cp) |
aisix (AISIX AI gateway data plane) |
charts/aisix |
no — independent product version; this repo is the source of truth (unlike aisix-cp, no dev-repo counterpart to sync from) |
ngxdig (eBPF flame-graph / diagnosis agent, DaemonSet) |
charts/ngxdig |
no — independent product version (source of truth: api7/ngx-flame) |
API7 EE supports several release lines at once (e.g. 3.9.x and 3.10.x). The charts must let each line receive patches without disturbing the others.
main= the latest (newest) release line, e.g. 3.10.x today.release/<major>.<minor>= a historical line, e.g. 3.9.x onrelease/3.9, 3.8.x onrelease/3.8. The EE major is always3; the minor is the "feature line".
main always equals the latest line. When the latest moves from 3.10 to 3.11, first git branch release/3.10 main to freeze the old line, then bump main to 3.11. This guarantees every superseded line has a branch the moment it is superseded.
When creating release/<x>.<y>, branch from the last published chart state of that line (which carries that line's values/templates) — not from main renumbered, since main may already carry config that only exists in a newer line.
For charts whose appVersion is the EE version (api7ee3, gateway):
- the chart's
major.minormirrors the EE minor:main(3.10) uses3.10.*,release/3.9uses3.9.*. - the chart's
patchis this line's own counter, decoupled from the app patch (the EE patch). Each line starts at.0. So achart 3.10.0may deployappVersion 3.10.1—appVersionis the source of truth for the image version; the chart patch only means "the Nth chart on this line".
Why: in the shared charts.api7.ai index, 3.9.x < 3.10.x, so a plain helm upgrade always stays on the newest line and is never dragged back to an older line by a later patch release, while users who want to stay on an older line pin it with --version '~3.9'. An opaque linear scheme (e.g. 0.18.x) would let a later 3.9 patch sort above 3.10 and break that ordering.
Cost: within a single line the chart can no longer use a major bump for breaking changes — but a maintenance line should only take backward-compatible bug fixes; breaking changes ride the next minor (a new EE line).
For independently-versioned sub-charts (ingress-controller, developer-portal-fe): their version axis is not the EE minor, so they are shared across EE lines and not forked per line by default. Only fork them onto their own product minor when an older EE line must ship a different version of them and keep patching it.
The AISIX charts (aisix-cp, aisix) release as a pair, on the AISIX OP version axis rather than the EE one: at each OP release both get version and appVersion set to that release. appVersion pins the image tag on both sides, so the two charts must never carry different appVersions — a control plane and a data plane from different releases is not a combination we ship.
When releasing the chart for EE X.Y.Z:
X.Yis the latest line (==main's current line) → editmain.- otherwise → edit
release/X.Y(cut it first per "When to cut a line" if it does not exist).
Operational rule: on any release/* branch, only bump the charts you actually intend to re-release for that line; leave the rest at their existing versions so CR_SKIP_EXISTING skips them — this avoids colliding numbers in a sub-chart's linear version space.
.github/workflows/release.yaml runs chart-releaser (submodule helm/chart-releaser-action) on push to main or release/**: it creates a GitHub Release + .tgz asset per new version and merges the update into the gh-pages index.yaml. Notes:
- triggers include
mainandrelease/**, so a push to an older line publishes its own charts. - top-level
concurrency: { group: helm-chart-release, cancel-in-progress: false }: all lines share one index, so releases must be serialized and an in-progress run must never be cancelled, or a release could be dropped. CR_SKIP_EXISTING: true: already-released versions are skipped and the index is merged (not overwritten), so multiple branches are safe.- the GitHub Release tag is
<chart>-<version>(e.g.gateway-3.9.0); the numbering rule keeps it unique across lines. - a green release run does not mean published. chart-releaser pushes one
gh-pagescommit per chart it releases, and pushes landing seconds apart get throttled: the Pages build is rejected before it runs (status: errored,duration: 0) while the workflow still reports success and the GitHub Release still exists.charts.api7.aithen keeps serving the previousindex.yaml, sohelm repo updatenever sees the new chart — and nothing alerts. Releasing two lines together (amainPR and arelease/*PR merged back to back) reliably triggers it. After any release checkgh api repos/api7/api7-helm-chart/pages/builds --jq '.[0]'; on aduration: 0error re-run it withgh api --method POST repos/api7/api7-helm-chart/pages/builds— the same commit builds fine on retry.gh-pages'sindex.yamlis the source of truth for what chart-releaser produced; the served copy tells you what users can actually install. ci.yaml(lint / ct install / helm-docs) also coversrelease/**.- after editing a chart, regenerate every chart's
README.mdwithhelm-docs --chart-search-root=charts— the CI check diffs all of them, not just the one you touched. Build the binary the way CI does,go install github.com/norwoodj/helm-docs/cmd/helm-docs@v1.13.1, and keep it first onPATH(thehelm-docspre-commit hook runs whatever it finds there). A released helm-docs binary — any version, v1.13.1's own tarball included — has a version string baked in and therefore appends anAutogenerated from chart metadata...footer to every README that has noREADME.md.gotmpl, which CI then rejects; ago installbuild carries no version string and omits the footer.
The gateway chart's values.yaml/configmap mirror the gateway's conf/config-default.yaml, except for config the control plane owns and delivers to the DP at runtime via the heartbeat config payload / etcd. Never expose these in the chart (a chart-set value would be overridden or would fight the CP):
apisix.data_encryptionandapisix.ssl.key_encrypt_salt— per-gateway-group keyring (CPGetDataplaneConfig)api7ee.consumer_proxy,api7ee.developer_proxy,api7ee.telemetry— CP system settings- dynamic service discovery (
api7_discovery, K8s/Nacos) — configured per gateway group in the Dashboard - the enabled
plugins/stream_pluginslist — CP manages it through the etcd/pluginskey, including custom plugins
When adding values, keep defaults identical to config-default.yaml so a default render changes nothing. To verify a rendered config is valid, extract config.yaml from the rendered configmap and run it through the real image: docker run --rm --entrypoint sh -v $PWD/config.yaml:/usr/local/apisix/conf/config.yaml api7/api7-ee-3-gateway:<ver> -c 'apisix init' — the config schema is validated at init.
config.yaml is rendered from developerPortal.config, which carries the application's whole config schema (owned by api7/api7ee-developer-portal, apps/site/src/lib/config/schema.ts). Do not hoist schema fields into their own .Values keys — a dedicated key plus template plumbing per field mirrors that schema, drifts on every application release, and leaves two ways to set one field. Only the connection settings the chart itself owns are templated, and they are merged over the block, so a user cannot replace the ${PORTAL_TOKEN} / ${DB_URL} / ${AUTH_SECRET} placeholders with literal credentials — those placeholders stay in the ConfigMap and resolve from Secret-backed env vars at startup.
That is not a licence to skip chart releases: pass-through only means a user is never blocked on one, while values.yaml is still how they find out an option exists. When the application adds config worth surfacing, add it under developerPortal.config and cut a chart release. Write it as a commented example rather than a live default — a live default pins the application's default of that day into the chart and keeps overriding it after the application moves on.
To verify a rendered config, run it through the image's own loader: docker run --rm -v $PWD/config.yaml:/app/apps/site/config.yaml -e PORTAL_TOKEN=x -e DB_URL=x -e AUTH_SECRET=$(head -c 32 /dev/zero | tr '\0' a) --entrypoint sh api7/api7-ee-developer-portal-fe:<ver> -c 'node preflight.js' — reaching the Portal URL: line means the config validated (the portal/database checks after it need a live backend). That mount path is fixed: the standalone server chdirs into apps/site, and preflight looks there.
- Follow the latest line:
helm repo update && helm upgrade <release> api7/<chart>(picks the highest version, always the newest EE line). - Pin an older line:
helm upgrade <release> api7/<chart> --version '~3.9'(stays on 3.9.x and takes that line's chart patches without jumping to 3.10).