Protect Your Users: PII Redaction in AI Requests with Kong AI Gateway 2.0
One-line summary: By the end of this tutorial, you'll have Kong AI Gateway 2.0 automatically stripping PII from every LLM request using the ai-sanitizer policy. Names, emails and phone numbers get swapped for synthetic v
One-line summary: By the end of this tutorial, you'll have Kong AI Gateway 2.0 automatically stripping PII from every LLM request using the
ai-sanitizerpolicy. Names, emails and phone numbers get swapped for synthetic values, and your application code doesn't change. Everything is configured declaratively withkongctl.
What You'll Build
A Kong AI Gateway 2.0 deployment, managed from Konnect, with a data plane running on a local kind cluster:
-
An AI Gateway (
kong-ai-tutorial) with an OpenAI model provider and agpt-4omodel exposed through an OpenAI-compatible endpoint. -
An
ai-sanitizerpolicy that sends every incoming message to Kong's AI PII Anonymizer service. This is an NLP service, not an LLM, and it runs next to the gateway in the cluster. PII is detected and replaced before the request reaches OpenAI.
The
syntheticredact mode replaces real PII with realistic fake values of the same type (a real name becomes a different name, a real phone number becomes a different phone number). The LLM still gets usable context, and your users' real data never leaves your gateway.
This tutorial is fully self-contained. You start from an empty Konnect organization and an empty machine, and you tear everything down at the end.
Prerequisites
Tools
| Tool | Version | Install |
|---|---|---|
| Docker | 24+ | docs.docker.com |
| kind | 0.23+ | kind.sigs.k8s.io |
| kubectl | 1.29+ | kubernetes.io |
| Helm | 3.14+ | helm.sh |
| kongctl | 1.17+ | developer.konghq.com/kongctl |
| OpenSSL | any | Pre-installed on macOS and most Linux distros |
| HTTPie | 3.2+ | httpie.io |
| jq | 1.6+ | jqlang.github.io/jq |
| k9s (optional) | 0.32+ | k9scli.io |
Quick check:
docker --version && kind --version && kubectl version --client
helm version --short && kongctl version && openssl version
http --version && jq --version
Create a working directory. Every file in this tutorial is created here, and every command is run from here:
mkdir -p ~/kong-ai-pii && cd ~/kong-ai-pii
Accounts / Keys
-
Kong Konnect with AI Gateway access. The
ai-sanitizerpolicy requires an Enterprise entitlement. Sign up at cloud.konghq.com. - OpenAI API key: platform.openai.com/api-keys
- Kong AI PII Anonymizer image access: see below.
⚠️ AI PII Anonymizer Service: Private Registry Access Required
The ai-sanitizer policy depends on a service that Kong distributes through a private Cloudsmith registry. You need to request access before you can pull the image.
- Contact Kong Support and ask for access to the AI PII Anonymizer service images.
- Once you're approved, Kong gives you a registry token. Export it and log in:
export CLOUDSMITH_TOKEN="<your-token-from-kong-support>"
echo "$CLOUDSMITH_TOKEN" | docker login docker.cloudsmith.io \
--username kong/ai-pii \
--password-stdin
- Check that you can pull the image:
docker pull docker.cloudsmith.io/kong/ai-pii/service:v0.2.2-en
At the time of writing,
v0.2.2is the latest image. Each language/locale has its own image. This tutorial usesen, but you can list the others with crane:crane ls docker.cloudsmith.io/kong/ai-pii/service | grep v0.2.2 # v0.2.2-de v0.2.2-en v0.2.2-es v0.2.2-fr v0.2.2-it v0.2.2-ja # v0.2.2-ko v0.2.2-nl v0.2.2-pt v0.2.2-th v0.2.2-trStill waiting for access? Complete Steps 1–6, then jump to Alternative: ai-request-transformer. That approach gets you PII scrubbing today, with no private image, while your access request is being processed.
Overview
- Create a kind cluster
- Create a Konnect System Account and access token
- Create the AI Gateway with
kongctl - Add the OpenAI model provider and
gpt-4omodel - Create the Kubernetes namespace and data plane certificate secret
- Install the AI Gateway data plane with Helm and run a baseline test
- Deploy the AI PII Anonymizer service
- Apply the
ai-sanitizerpolicy withkongctl - Test the redaction
- Clean up
Step 1: Create a Kind Cluster
kind create cluster --name kong-ai
Check that the cluster is up:
kubectl cluster-info --context kind-kong-ai
Expected:
Kubernetes control plane is running at https://127.0.0.1:XXXXX
If you want to watch the cluster as you work, open k9s:
k9s --context kind-kong-ai
💡 In k9s, press
0to show all namespaces, type:podsto list pods, and pressCtrl+Cto exit.
Step 2: Create a System Account, Admin Role, and Token
kongctl needs a Konnect token. Instead of using your personal credentials, create a dedicated System Account, which is the recommended way to handle automated access in Konnect.
Create the System Account
Click Manage Organization in the top left corner.
Note: Konnect moved this menu in a UI update released in June 2026.
Select System Accounts in the top menu, then click Create System Account.
- Name it
kong-ai-tutorial-sa - Add a description
- Click Create
Assign the Admin Role
Open the system account, select Role Assignment, then click Add Role.
- Entity Type: AI Gateways
- Select your region (this tutorial uses EU)
- Scope it to all gateways. The
kong-ai-tutorialAI Gateway doesn't exist yet becausekongctlcreates it in Step 3. - Role: AI Gateways Admin and AI Gateways Creator
- Click Save
Generate an Access Token
Click Manage Tokens in the top right of the system account page.
Then click Generate Token.
- Name it
kongctl-token - Set an expiry if you like (90 days is a sensible choice)
Copy the token straight away, because Konnect won't show it again.
export KONNECT_TOKEN="spat_..."
Step 3: Create the AI Gateway
Generate a data plane certificate
In hybrid mode, the data plane authenticates to Konnect with a certificate. Generate a self-signed one now. In the next step you register the public half with the AI Gateway, and in Step 5 you mount both halves into the cluster.
openssl req \
-new \
-x509 \
-nodes \
-newkey rsa:2048 \
-keyout tls.key \
-out tls.crt \
-days 365 \
-subj "/CN=ai-gateway-dp"
Declare the gateway
Create ai-gateway.yaml. This file grows through the tutorial and ends up holding the whole gateway definition.
# ai-gateway.yaml
ai_gateways:
- ref: kong-ai-tutorial
name: kong-ai-tutorial
display_name: "Kong AI Tutorial"
description: "AI Gateway for the PII sanitization tutorial"
deployment_type: hybrid
data_plane_certificates:
- ref: kind-dp-cert
title: "kind-data-plane"
description: "Certificate used by the kind data plane"
cert: !file ./tls.crt
!file ./tls.crt tells kongctl to read the certificate from disk, so you never paste PEM blocks into YAML.
Apply it
kongctl apply \
--region eu \
--auto-approve \
--pat "$KONNECT_TOKEN" \
-f ai-gateway.yaml
💡 Leave out
--auto-approveto review the plan before it runs, or usekongctl diff -f ai-gateway.yaml --region eu --pat "$KONNECT_TOKEN"to preview changes without applying them.
Check that the gateway exists:
kongctl get ai-gateways --region eu --pat "$KONNECT_TOKEN"
Step 4: Add the OpenAI Model Provider and Model
Set your OpenAI key
If you don't have one yet, create an API key in your OpenAI account and export it. Navigate to your OpenAI account https://platform.openai.com/api-keys
export OPENAI_API_KEY="sk-..."
Extend the gateway config
Append a model provider (how to reach and authenticate to OpenAI) and a model (what clients can call, and where the gateway sends it) to ai-gateway.yaml. The full file now looks like this:
# ai-gateway.yaml
ai_gateways:
- ref: kong-ai-tutorial
name: kong-ai-tutorial
display_name: "Kong AI Tutorial"
description: "AI Gateway for the PII sanitization tutorial"
deployment_type: hybrid
data_plane_certificates:
- ref: kind-dp-cert
title: "kind-data-plane"
description: "Certificate used by the kind data plane"
cert: !file ./tls.crt
ai_gateway_model_providers:
- ref: openai
ai_gateway: kong-ai-tutorial
name: openai
display_name: "OpenAI"
type: openai
config:
auth:
type: basic
headers:
- name: Authorization
value: !secret {parts: ["Bearer ", !env OPENAI_API_KEY]}
ai_gateway_models:
- ref: gpt-4o
ai_gateway: kong-ai-tutorial
name: gpt-4o
display_name: "GPT-4o"
type: model
enabled: true
capabilities:
- generate
formats:
- type: openai
config:
route:
paths:
- /v1
model:
body_param: model
values:
- gpt-4o
targets:
- name: gpt-4o
provider: openai
config:
type: openai
A few things to note:
-
ai_gateway: kong-ai-tutorialpoints each child resource at the gateway'sref. -
!secret {parts: ["Bearer ", !env OPENAI_API_KEY]}builds theAuthorizationheader from your environment when you apply. The key never goes into the file. -
config.route.modelroutes on themodelfield in the request body, so a client sending"model": "gpt-4o"to/v1/chat/completionsreaches this model.
Apply it
kongctl apply \
--region eu \
--auto-approve \
--pat "$KONNECT_TOKEN" \
-f ai-gateway.yaml
kongctl apply only creates and updates, so running it again is safe. The gateway and certificate from Step 3 are left alone, and only the new provider and model are created.
You should now see both an AI Provider and Model in your Konnect account
Step 5: Create the Kubernetes Namespace and Certificate Secret
Load the certificate pair from Step 3 into the cluster so the data plane can present it to Konnect:
kubectl create namespace kong
kubectl create secret tls kong-cluster-cert \
--cert=./tls.crt \
--key=./tls.key \
--namespace kong
Check it:
kubectl get secret kong-cluster-cert -n kong
Expected:
NAME TYPE DATA AGE
kong-cluster-cert kubernetes.io/tls 2 5s
Step 6: Install the AI Gateway Data Plane with Helm
Add the Helm repo
helm repo add kong https://charts.konghq.com
helm repo update
Find your cluster endpoints
The data plane needs your AI Gateway's control plane and telemetry endpoints.
To find them lets use the following command
kongctl get ai-gateway "Kong AI Tutorial" --pat "$KONNECT_TOKEN" --output yaml
Your output will look something like this
config_version: "1106953144"
created_at: "2026-09-30T08:57:47Z"
deployment_type: hybrid
description: AI Gateway for the PII sanitization tutorial
display_name: Kong AI Tutorial
endpoints:
configuration: https://b0caf4acb8.eu.cp.konghq.com
telemetry: https://b0caf4acb8.eu.tp.konghq.com
id: c4f5cddc-edbb-4b97-9175-da3938d0be5e
labels:
KONGCTL-namespace: default
min_runtime_version: "2.1"
name: kong-ai-tutorial
runtime_auto_upgrade: true
updated_at: "2026-09-30T08:57:47Z"
You are looking for the endpoints field and will use both the configuration and telemetry endpoints in the next step.
Create the values file
Create kong-values.yaml and replace the values with your endpoints from above
💡 NOTE: The endpoints must NOT contain the
httpspart of the endpoint as the dataplane will create a websocker and use thewssprotocol instead
# kong-values.yaml
ingressController:
enabled: false
image:
repository: kong/kong-ai-gateway
tag: "2.1.0"
secretVolumes:
- kong-cluster-cert
env:
role: data_plane
database: "off"
konnect_mode: "on"
vitals: "off"
cluster_mtls: pki
cluster_control_plane: "<endpoints.configuration>:443"
cluster_server_name: "<endpoints.configuration>"
cluster_telemetry_endpoint: "<endpoints.telemetry>:443"
cluster_telemetry_server_name: "<endpoints.telemetry>"
cluster_cert: /etc/secrets/kong-cluster-cert/tls.crt
cluster_cert_key: /etc/secrets/kong-cluster-cert/tls.key
lua_ssl_trusted_certificate: system
proxy_access_log: "off"
dns_stale_ttl: "3600"
resources:
requests:
cpu: 1
memory: "2Gi"
proxy:
enabled: true
type: ClusterIP
admin:
enabled: false
manager:
enabled: false
Install
helm upgrade -i kong kong/kong-ai-gateway \
--namespace kong \
--values kong-values.yaml
Check that the pod is up
kubectl get pods -n kong
Expected:
NAME READY STATUS RESTARTS AGE
kong-kong-ai-gateway-7896b9bb78-7fwkz 1/1 Running 0 90s
In Konnect, the node should now appear as Connected under your AI Gateway's Data Plane Nodes.
Expose the proxy locally
Find the proxy service and port-forward it to localhost:8000:
kubectl get svc -n kong
kubectl port-forward -n kong svc/kong-kong-ai-gateway-proxy 8000:80 &
Quick call to test it all out
http --body POST localhost:8000/v1/chat/completions \
model=gpt-4o \
messages:='[
{"role":"user","content":"What is Kong AI Gateway"}
]' | jq -r '.choices[0].message.content'
Baseline test (no PII protection yet)
Send a message that contains PII so you can see what OpenAI receives before adding the policy:
http --body POST localhost:8000/v1/chat/completions \
model=gpt-4o \
messages:='[
{"role":"system","content":"You are a helpful assistant. Repeat back the details the user gives you."},
{"role":"user","content":"My name is John Doe, my phone number is 123-456-7890"}
]' | jq -r '.choices[0].message.content'
The reply repeats John Doe and 123-456-7890, because OpenAI received the real data. The next two steps fix that.
Step 7: Deploy the AI PII Anonymizer Service
The ai-sanitizer policy hands PII detection to a dedicated NLP service. Deploy it as its own Deployment and Service in the kong namespace so the gateway can reach it at a stable DNS name.
Create a pull secret for the private registry
kubectl create secret docker-registry cloudsmith-kong-pii \
--docker-server=docker.cloudsmith.io \
--docker-username="kong/ai-pii" \
--docker-password="$CLOUDSMITH_TOKEN" \
--namespace kong
Deploy the service
Create pii-service.yaml:
# pii-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kong-pii-service
namespace: kong
spec:
replicas: 1
selector:
matchLabels:
app: kong-pii-service
template:
metadata:
labels:
app: kong-pii-service
spec:
imagePullSecrets:
- name: cloudsmith-kong-pii
containers:
- name: pii-service
image: docker.cloudsmith.io/kong/ai-pii/service:v0.2.2-en
ports:
- containerPort: 8080
env:
- name: GUNICORN_WORKERS
value: "2"
resources:
requests:
memory: "1Gi" # the NLP model needs at least 600MB
cpu: "250m"
limits:
memory: "1.5Gi"
cpu: "500m"
readinessProbe:
httpGet:
path: /llm/v1/status
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: kong-pii-service
namespace: kong
spec:
selector:
app: kong-pii-service
ports:
- port: 8080
targetPort: 8080
Apply it and wait for it to become ready:
kubectl apply -f pii-service.yaml
kubectl rollout status deployment/kong-pii-service -n kong
On first start the NLP model takes about 20–30 seconds to load. You're ready once the readiness probe passes.
Check that the service is reachable from inside the cluster:
kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never -n kong -- \
curl -s http://kong-pii-service:8080/llm/v1/status
Expected:
{"status":"ok","supported_languages":["en"]}
Step 8: Apply the ai-sanitizer Policy
Put the policy in its own file, pii-policy.yaml, so you can swap it out later (see the Alternative section):
# pii-policy.yaml
ai_gateway_policies:
- ref: pii-sanitizer
ai_gateway: kong-ai-tutorial
name: pii-sanitizer
display_name: "PII Sanitizer"
type: ai-sanitizer
enabled: true
global: true
config:
host: kong-pii-service.kong.svc.cluster.local
port: 8080
anonymize:
- general
- phone
- creditcard
- nationalid
- bank
- medical
- ip
redact_type: synthetic
stop_on_error: true
recover_redacted: false
| Setting | What it does |
|---|---|
global: true |
Applies the policy to every model on the gateway |
anonymize |
The PII categories to detect (general covers names, emails, addresses, and so on) |
redact_type: synthetic |
Replaces PII with realistic fake values rather than [REDACTED] placeholders |
stop_on_error: true |
Fails closed. If the anonymizer can't be reached, the request is blocked instead of sent unredacted. |
recover_redacted: false |
Doesn't swap the original values back into the LLM response |
Apply both files together, so kongctl sees the gateway the policy refers to:
kongctl apply \
--region eu \
--auto-approve \
--pat "$KONNECT_TOKEN" \
-f ai-gateway.yaml \
-f pii-policy.yaml
Check its been applied
kongctl get ai-gateway policies \
--gateway-name "Kong AI Tutorial" \
--region eu \
--pat "$KONNECT_TOKEN"
NAME TYPE ENABLED ID
pii-sanitizer ai-sanitizer true 3bb7…
Step 9: Test the Redaction
Send the same request as the baseline:
http --body POST localhost:8000/v1/chat/completions \
model=gpt-4o \
messages:='[
{"role":"system","content":"You are a helpful assistant. Repeat back the details the user gives you."},
{"role":"user","content":"My name is John Doe, my phone number is 123-456-7890"}
]' | jq -r '.choices[0].message.content'
This time the LLM replies with synthetic stand-ins. It never saw John's real name or real phone number, because the gateway replaced them before the request left your cluster.
Example response:
You've mentioned that your name is Karen Robinson and your phone number is 217.948.9102 extension 461.
Step 10: Clean Up
Stop the port-forward:
kill %1
Remove the Konnect resources. kongctl delete removes everything declared in the files:
kongctl delete \
--region eu \
--auto-approve \
--pat "$KONNECT_TOKEN" \
-f ai-gateway.yaml \
-f pii-policy.yaml
Delete the kind cluster, which also removes the data plane and the PII service:
kind delete cluster --name kong-ai
Alternative: ai-request-transformer
Waiting for Cloudsmith access? You can get a similar result today with the ai-request-transformer policy. It uses an LLM to scrub PII instead of a dedicated NLP service.
It's less accurate than the NLP-based ai-sanitizer (an LLM can occasionally miss PII or hallucinate), and each request costs a little more time and money because of the extra LLM call. In exchange, it works out of the box: no extra service, no private registry, and no extra infrastructure.
How it works
ai-request-transformer sends the user message to a fast, cheap LLM (gpt-4o-mini) with a system prompt that tells it to scrub PII. The LLM returns the cleaned message, and the gateway swaps it in before forwarding the request to your model.
Prerequisites
Complete Steps 1–6, then skip Steps 7–8 and continue here.
Switch the main model to gpt-5-mini
NOTE:
gpt-4otends to refuse prompts that contain personal information, even after the information has been replaced with placeholder tokens, so this example doesn't work with it. Use a newer model such asgpt-5-mini.
In ai-gateway.yaml, replace the ai_gateway_models block with:
ai_gateway_models:
- ref: gpt-5-mini
ai_gateway: kong-ai-tutorial
name: gpt-5-mini
display_name: "GPT-5 Mini"
type: model
enabled: true
capabilities:
- generate
formats:
- type: openai
config:
route:
paths:
- /v1
model:
body_param: model
values:
- gpt-5-mini
targets:
- name: gpt-5-mini
provider: openai
config:
type: openai
Create the policy
Create pii-policy-alt.yaml:
# pii-policy-alt.yaml
ai_gateway_policies:
- ref: ai-request-transformer
ai_gateway: kong-ai-tutorial
name: ai-request-transformer
display_name: "AI Request Transformer"
type: ai-request-transformer
enabled: true
global: true
config:
prompt: |
You are a PII scrubber. Find and replace all personally identifiable
information in the user message with safe placeholder tokens:
- Full names → [NAME]
- Email addresses → [EMAIL]
- Phone numbers → [PHONE]
- Physical addresses → [ADDRESS]
- National ID numbers → [ID_NUMBER]
- Payment card numbers → [CARD]
Return ONLY the scrubbed message text. No explanation. No commentary.
llm:
route_type: llm/v1/chat
auth:
header_name: Authorization
header_value: !env OPENAI_AUTH_HEADER
model:
provider: openai
name: gpt-4o-mini
options:
max_tokens: 512
temperature: 0
Apply it
Use kongctl sync instead of apply here. sync also deletes managed resources that are no longer in your files, so the old gpt-4o model is removed along with the ai-sanitizer policy, if you created it:
kongctl sync \
--region eu \
--auto-approve \
--pat "$KONNECT_TOKEN" \
-f ai-gateway.yaml \
-f pii-policy-alt.yaml
Test it
http --body POST localhost:8000/v1/chat/completions \
model=gpt-5-mini \
messages:='[
{"role":"system","content":"You are a helpful assistant. Repeat back the details the user gives you."},
{"role":"user","content":"My name is John Doe, my phone number is 123-456-7890"}
]' | jq -r '.choices[0].message.content'
Expected:
You said your name is [NAME] and your phone number is [PHONE].
When your ai-sanitizer access comes through, complete Step 7, then run one kongctl sync -f ai-gateway.yaml -f pii-policy.yaml to switch to the NLP-based version. Remember to include --region eu --pat "$KONNECT_TOKEN" as in the other commands. To clean up, include pii-policy-alt.yaml in the kongctl delete command in Step 10.
ai-sanitizer |
ai-request-transformer |
|
|---|---|---|
| Detection method | NLP model (based on Microsoft Presidio) | LLM prompt |
| Accuracy | High, deterministic | Good, non-deterministic |
| Latency added | ~10–50ms | ~200–500ms (an extra LLM round trip) |
| Cost per request | Nothing beyond infrastructure | A small LLM token cost |
| Replacement style | Synthetic values or placeholders | Placeholders (whatever the prompt says) |
| Extra service required | Yes (private registry) | No |
Troubleshooting
kongctl returns 401 / Unauthenticated
- Check that
KONNECT_TOKENis set and starts withspat_:printenv KONNECT_TOKEN | cut -c1-5 - Check that the system account has the Admin role.
- Check that
--regionmatches your Konnect region. Without it,kongctluses the US region.
kongctl reports unknown field ...
kongctl validates files against its schema before it contacts Konnect. Use kongctl explain to see the expected structure, for example:
kongctl explain ai_gateway.models --extended
kongctl scaffold ai_gateway.policies
OpenAI returns 401 through the gateway
The provider was created without a valid key. Export OPENAI_API_KEY again and re-apply with --write-secrets, which forces kongctl to push write-only secret fields again:
kongctl apply --region eu --auto-approve --pat "$KONNECT_TOKEN" \
--write-secrets -f ai-gateway.yaml
Data plane shows as disconnected in Konnect
- Check the pod logs:
kubectl logs -n kong -l app.kubernetes.io/instance=kong - Check that
tls.crtis the same certificate registered inai-gateway.yaml. If you regenerated it, re-run bothkongctl applyand thekubectl create secretstep. - Check the four endpoint values in
kong-values.yaml.
PII service pod stuck in ImagePullBackOff
The Cloudsmith pull secret isn't set up correctly. Check the events:
kubectl describe pod -n kong -l app=kong-pii-service | grep -A5 Events
Delete and recreate the cloudsmith-kong-pii secret with the correct token from Kong Support, then restart the deployment with kubectl rollout restart deployment/kong-pii-service -n kong.
Requests fail because stop_on_error: true is blocking them
The policy can't reach the PII service. Test DNS and connectivity from the gateway pod with an ephemeral debug container:
POD=$(kubectl get pods -n kong -o name | grep -v kong-pii-service | head -1)
kubectl debug -n kong -it "$POD" \
--image=curlimages/curl \
-- \
curl -sv http://kong-pii-service:8080/llm/v1/status
If this fails, confirm that both pods are in the kong namespace and that the Service selector matches the Deployment labels.
port-forward fails with "address already in use"
Something else is using port 8000. Stop it, or forward a different local port (for example 8080:80) and use that port in your http commands.
Resources
- 📖 kongctl documentation
- 📖 Sanitize LLM responses too
- 📖 AI Sanitizer reference
- 📖 AI Request Transformer reference
✏️ Drafted with KewBot (AI), edited and approved by Andrew Kew.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.











