Configure TCP/UDP Proxying
API7 Gateway can proxy Layer 4 (TCP/UDP) traffic in addition to HTTP traffic. This enables you to use the gateway as a unified entry point for non-HTTP protocols such as MySQL, Redis, MQTT, and custom TCP services.
This guide walks through configuring a TCP proxy to a MySQL database as an example. The same approach applies to any TCP or UDP service.
Prerequisites
- An API7 Enterprise instance is running.
- A Gateway Group is created and a Gateway instance is running.
- A token from the Dashboard.
- A MySQL client is installed if you want to validate the sample TCP proxy with
mysql.
Start a Sample TCP Upstream
Start the same sample MySQL server used in the API7 Enterprise TCP proxy best-practice guide:
docker run -d \
--name mysql \
--network host \
-e MYSQL_ROOT_PASSWORD=password \
mysql:8.4 \
mysqld --mysql-native-password=ON
The examples below assume the gateway can reach the Docker host at host.docker.internal. If your environment uses a different host address, replace host.docker.internal with that address.
Ensure a Stream Proxy Port Is Available
Before traffic can reach a stream route, the gateway must already be listening on a TCP or UDP port for stream traffic.
Admin API and ADC can create stream services and stream routes, but they do not create or expose the gateway listener itself. If your API7 Enterprise deployment already provides an L4 listener, reuse that port in server_port. If not, add one in the gateway runtime configuration and redeploy or restart the gateway so the new listener is exposed.
If you manage the gateway runtime directly, add the port to config.yaml as follows:
apisix:
stream_proxy:
only: false
tcp:
- 2000
If the gateway runs in Docker, also publish the same port from the container to the host. For example, recreate or redeploy the gateway container with -p 2000:2000.
For Kubernetes deployments, add the stream proxy ports to your Helm values, ensure the Service exposes them, and redeploy the gateway.
Create a Stream Service
Once a stream listener is available on the gateway, create a service with type stream and configure the upstream.
- Admin API
- ADC
curl -k "https://localhost:7443/apisix/admin/services/mysql-service?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "mysql-service",
"type": "stream",
"upstream": {
"scheme": "tcp",
"nodes": [
{
"host": "host.docker.internal",
"port": 3306,
"weight": 100
}
]
}
}'
services:
- name: mysql-service
upstream:
scheme: tcp
nodes:
- host: host.docker.internal
port: 3306
weight: 100
stream_routes:
- name: mysql-route
server_port: 2000
Create a Stream Route
Create a stream route that matches traffic on the stream proxy port and forwards it to the upstream.
- Admin API
- ADC
curl -k "https://localhost:7443/apisix/admin/stream_routes/mysql-route?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "mysql-route",
"server_port": 2000,
"service_id": "mysql-service"
}'
services:
- name: mysql-service
upstream:
scheme: tcp
nodes:
- host: host.docker.internal
port: 3306
weight: 100
stream_routes:
- name: mysql-route
server_port: 2000
server_port must match an existing gateway stream listener configured under stream_proxy.tcp or stream_proxy.udp. In the Admin API example, service_id references the stream service that contains the upstream configuration.
For ADC workflows, define stream routes under the parent service. To validate the examples end to end, make sure the same stream port is configured on the gateway and exposed to clients by your deployment.
Validate the Configuration
After the gateway has been redeployed or restarted with the stream listener enabled and exposed, test the TCP proxy by connecting to the gateway's stream proxy port:
# MySQL example
mysql -h 127.0.0.1 -P 2000 -u root -p
If the connection succeeds and you can interact with the MySQL server, the stream route is working correctly.
For a generic TCP test:
# Using netcat
nc -zv 127.0.0.1 2000
Add Stream Plugins
Stream routes support a limited set of plugins. For example, you can use limit-conn to add connection rate limiting:
- Admin API
- ADC
curl -k "https://localhost:7443/apisix/admin/stream_routes/mysql-route?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "mysql-route",
"server_port": 2000,
"service_id": "mysql-service",
"plugins": {
"limit-conn": {
"conn": 100,
"burst": 50,
"default_conn_delay": 0.1,
"key": "remote_addr",
"key_type": "var"
}
}
}'
PUT replaces the entire stream route, so re-send the existing fields (name, server_port, service_id) along with the new plugins block.
services:
- name: mysql-service
upstream:
scheme: tcp
nodes:
- host: mysql.example.com
port: 3306
weight: 1
stream_routes:
- name: mysql-route
server_port: 2000
plugins:
limit-conn:
conn: 100
burst: 50
default_conn_delay: 0.1
key: "$remote_addr"
key_type: var
Configure TLS for Stream Routes
To proxy TLS-encrypted TCP connections with SNI-based routing:
-
Enable a TLS stream proxy port:
config.yamlapisix:
stream_proxy:
tcp:
- addr: 9443
tls: true -
Create stream routes with the
snifield to route based on the client's TLS Server Name Indication.
Next Steps
- Stream Routes: Conceptual overview of stream routes.
- gRPC Proxy: Proxy gRPC traffic (Layer 7).
- WebSocket Proxy: Proxy WebSocket connections.