Docs
Ingress ControllerManage traffic and APIsCustom PluginsDeploy Wasm Plugins

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

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 address

Steps

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.wasm

Save 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.yaml

In the working file, append the volume, mount, and plugin entries while preserving every existing list entry and unrelated value:

apisix-values.wasm-guide.yaml
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.wasm

If 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=5m

If 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:

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: 5678
kubectl apply -f wasm-plugin-upstream.yaml
kubectl rollout status deployment/wasm-plugin-guide-upstream --namespace aic --timeout=2m

Step 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)"
wasm-route.yaml
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: 80

Step 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.yaml

Validate

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.yaml

Restore 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 aic

Stop 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_MARKER

Troubleshoot

SymptomCauseFix
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.