Deploy Wasm Plugins
Learn how to load Wasm custom plugins into gateways in a Kubernetes environment and apply them to routes using APISIX Ingress Controller.
Applies to:APISIX Ingress Controller (drives Apache APISIX)
Goal
Load a WebAssembly (Wasm) plugin into the gateway and apply it to a route so that the plugin runs on incoming requests.
APISIX supports WebAssembly (Wasm) plugins developed following the Proxy-Wasm specification, a specification that extends Wasm capabilities to proxies.
API7 Enterprise does not support custom Wasm plugins.
Before you begin
- Complete Set Up Ingress Controller and Gateway.
- You have
kubectlconfigured against the cluster used in the setup guide. - Follow the Write Plugin Logic in Go (opens in Apache APISIX docs) and Compile the Code into Wasm (opens in Apache APISIX docs) sections to prepare
wasm-plugin-guide/guide-wasm-log.wasm. - Choose a free local port for the gateway port-forward. The examples use
9080.
Set the variables this guide uses:
export GATEWAY_SERVICE_NAME="apisix-gateway" # your gateway's Service name in namespace aic
export GATEWAY_LOCAL_PORT="9080"
export GATEWAY_URL="http://127.0.0.1:${GATEWAY_LOCAL_PORT}" # kubectl port-forward, or your Service's addressSteps
Step 1: Load the Wasm plugin into the gateway
Confirm the guide will not replace existing resources, then create a ConfigMap from the compiled module:
for resource in configmap/guide-wasm-log deployment/wasm-plugin-guide-upstream service/wasm-plugin-guide-upstream; do
! kubectl get --namespace aic "${resource}" >/dev/null 2>&1 || { echo "Stop: ${resource} already exists"; exit 1; }
done
kubectl create configmap guide-wasm-log --namespace aic \
--from-file=guide-wasm-log.wasm=wasm-plugin-guide/guide-wasm-log.wasmSave the exact effective values with restricted permissions, then make a working copy. These files can contain secrets; do not commit or retain them:
umask 077
helm get values --namespace aic apisix --all -o yaml > apisix-values.before-wasm-guide.yaml
cp apisix-values.before-wasm-guide.yaml apisix-values.wasm-guide.yamlIn the working file, append the volume, mount, and plugin entries while preserving every existing list entry and unrelated value:
extraVolumes:
# Keep every existing volume.
- name: guide-wasm-log
configMap:
name: guide-wasm-log
extraVolumeMounts:
# Keep every existing mount.
- name: guide-wasm-log
mountPath: /usr/local/apisix/wasm/guide-wasm-log.wasm
subPath: guide-wasm-log.wasm
...
apisix:
wasm:
enabled: true
plugins:
# Keep every existing Wasm plugin.
- name: guide_wasm_log
priority: 7999
file: /usr/local/apisix/wasm/guide-wasm-log.wasmIf any of these lists already contain entries, append instead of replacing them. If guide_wasm_log already exists, stop and use the deployment's source-controlled configuration rather than taking ownership of it in this guide.
Upgrade the release:
helm upgrade apisix apisix/apisix --namespace aic \
-f apisix-values.wasm-guide.yaml
kubectl rollout status deployment/apisix --namespace aic --timeout=5mIf the upgrade or rollout fails, restore apisix-values.before-wasm-guide.yaml immediately and wait for the recovery rollout before troubleshooting.
Step 2: Create a local upstream
Save this deterministic upstream as wasm-plugin-upstream.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: wasm-plugin-guide-upstream
spec:
replicas: 1
selector:
matchLabels:
app: wasm-plugin-guide-upstream
template:
metadata:
labels:
app: wasm-plugin-guide-upstream
spec:
containers:
- name: echo
image: hashicorp/http-echo:1.0.0
args: ["-listen=:5678", "-text=wasm-plugin-upstream"]
ports:
- name: http
containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: wasm-plugin-guide-upstream
spec:
selector:
app: wasm-plugin-guide-upstream
ports:
- name: http
port: 80
targetPort: 5678kubectl apply -f wasm-plugin-upstream.yaml
kubectl rollout status deployment/wasm-plugin-guide-upstream --namespace aic --timeout=2mStep 3: Create a route with the Wasm plugin
Generate a non-secret marker, then create a route with the guide_wasm_log plugin. The placeholder is replaced only in the local manifest:
export WASM_MARKER="ic-wasm-guide-$(date +%s)"apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: guide-wasm-log
spec:
plugins:
- name: guide_wasm_log
config:
conf: REPLACE_WASM_MARKER
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: wasm-plugin-guide
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /wasm-plugin-guide
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: guide-wasm-log
backendRefs:
- name: wasm-plugin-guide-upstream
port: 80apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: wasm-plugin-guide
spec:
ingressClassName: apisix
http:
- name: wasm-plugin-guide
match:
paths:
- /wasm-plugin-guide
plugins:
- name: guide_wasm_log
enable: true
config:
conf: REPLACE_WASM_MARKER
backends:
- serviceName: wasm-plugin-guide-upstream
servicePort: 80Step 4: Apply the route configuration
Apply the manifest to your cluster:
sed -i.bak "s/REPLACE_WASM_MARKER/${WASM_MARKER}/g" wasm-route.yaml
kubectl apply -f wasm-route.yamlValidate
Expose the gateway Service on your local machine:
kubectl port-forward --namespace aic "svc/${GATEWAY_SERVICE_NAME}" "${GATEWAY_LOCAL_PORT}:80" &
PORT_FORWARD_PID=$!Send one request and require the exact controlled-upstream body:
test "$(curl --fail-with-body -sS --connect-timeout 3 --max-time 15 "${GATEWAY_URL}/wasm-plugin-guide")" = "wasm-plugin-upstream"Require the gateway log to contain the same unique marker configured on the route:
for attempt in {1..10}; do
kubectl logs deployment/apisix --namespace aic --since=1m 2>&1 | grep -F "guide_wasm_log marker=${WASM_MARKER}" >/dev/null && break
sleep 1
done
kubectl logs deployment/apisix --namespace aic --since=1m 2>&1 | grep -F "guide_wasm_log marker=${WASM_MARKER}"The exact body proves upstream continuity; the unique log line proves that this Wasm Plugin Context handled the request.
Roll back / Clean up
Delete the route and upstream first:
kubectl delete -f wasm-route.yaml
kubectl delete -f wasm-plugin-upstream.yamlRestore the exact values captured before the guide, wait for the rollout, and only then remove the ConfigMap:
helm upgrade apisix apisix/apisix --namespace aic \
-f apisix-values.before-wasm-guide.yaml
kubectl rollout status deployment/apisix --namespace aic --timeout=5m
kubectl delete configmap guide-wasm-log --namespace aicStop the port-forward and remove local files, including the sensitive Helm values:
kill "${PORT_FORWARD_PID}" 2>/dev/null || true
rm -f wasm-route.yaml wasm-route.yaml.bak wasm-plugin-upstream.yaml \
apisix-values.before-wasm-guide.yaml apisix-values.wasm-guide.yaml
unset GATEWAY_SERVICE_NAME GATEWAY_LOCAL_PORT GATEWAY_URL PORT_FORWARD_PID WASM_MARKERTroubleshoot
| Symptom | Cause | Fix |
|---|---|---|
| The gateway rollout fails. | The values file replaced an existing entry, the module is invalid, or it uses a host call unsupported by this APISIX version. | Restore the pre-guide values immediately, then inspect the complete preserved lists and gateway logs. |
curl returns 404 or 500. | The route is not reconciled, its PluginConfig is missing, or the module is not mounted at the configured path. | Confirm the route parent, extension reference, ConfigMap, absolute module path, and completed gateway rollout. |
| The body assertion fails. | The route points to another backend or the sample Deployment is not ready. | Check the Service name and require the sample Deployment rollout to succeed before retrying. |
| The gateway log has no matching marker. | Another Route matched, the plugin is not attached, or logs are sent somewhere other than the Deployment output. | Inspect the applied route and gateway logging destination, and search for the exact non-secret marker. |
| Cleanup rollout fails. | The backup is missing or belongs to another Helm release. | Keep the ConfigMap in place until the correct pre-guide values are recovered and the gateway rolls out successfully. |