Skip to main content

a6-plugin-traffic-split

Overview

The traffic-split plugin dynamically directs portions of traffic to different upstream services based on custom rules (match) and weighted distributions (weighted_upstreams). Use it for canary releases, blue-green deployments, and A/B testing — all without modifying DNS or load balancers.

When to Use

  • Canary release: gradually shift traffic to a new version (10% → 50% → 100%)
  • Blue-green deployment: switch traffic based on request headers or cookies
  • A/B testing: split traffic by user attributes (headers, query params, cookies)
  • Feature flags: route specific users to feature branches
  • Multi-version API: run multiple backend versions simultaneously

Plugin Configuration Reference (Route/Service)

Top-level

FieldTypeRequiredDefaultDescription
rulesarray[object]YesList of traffic splitting rules. Each rule has optional match and required weighted_upstreams.

rules[].match

FieldTypeRequiredDefaultDescription
matcharray[object]No[]Conditions to activate this rule. Empty = unconditional (all traffic uses weights).
match[].varsarray[array]NoVariable expressions: ["variable", "operator", "value"]. Uses Nginx variables. Multiple vars in one object = AND. Multiple objects in match = OR.

Operators: ==, ~=, >, <, >=, <=, ~~ (regex match), !~~, in, has, ! — see lua-resty-expr.

Common variables: arg_name (query param), http_header-name (request header), cookie_name (cookie value).

rules[].weighted_upstreams[]

FieldTypeRequiredDefaultDescription
upstream_idstring/integerNoID of a pre-configured upstream object. Use this to get health checks, retries, etc.
upstreamobjectNoInline upstream configuration (see below).
weightintegerNo1Traffic weight for this upstream.

If only weight is set (no upstream or upstream_id), traffic goes to the route's default upstream.

Inline upstream object

FieldTypeRequiredDefaultDescription
typestringNo"roundrobin"Load balancing: "roundrobin" or "chash".
nodesobjectYesBackend nodes as {"host:port": weight}.
timeoutobjectNo15 (seconds){"connect": N, "send": N, "read": N}
pass_hoststringNo"pass""pass" = client host, "node" = upstream node, "rewrite" = use upstream_host.
upstream_hoststringNoCustom Host header. Only works with pass_host: "rewrite".
namestringNoHuman-readable name for the upstream.

Not supported in inline upstream: service_name, discovery_type, checks, retries, retry_timeout, scheme. Use upstream_id for these features.

Step-by-Step: Enable traffic-split on a Route

1. Canary release — 20% to new version

a6 route create -f - <<'EOF'
{
"id": "canary-release",
"uri": "/api/*",
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream": {
"name": "new-version-v2",
"type": "roundrobin",
"nodes": {
"backend-v2:8080": 1
}
},
"weight": 2
},
{
"weight": 8
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"backend-v1:8080": 1
}
}
}
EOF

Result: 20% traffic → backend-v2, 80% → backend-v1 (route default).

2. Blue-green deployment — header-based switching

a6 route create -f - <<'EOF'
{
"id": "blue-green",
"uri": "/api/*",
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [
["http_x-canary", "==", "true"]
]
}
],
"weighted_upstreams": [
{
"upstream": {
"name": "green-env",
"type": "roundrobin",
"nodes": {
"green-backend:8080": 1
}
},
"weight": 1
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"blue-backend:8080": 1
}
}
}
EOF

Result: Requests with header x-canary: true → green, all others → blue.

3. Increase canary to 50%

a6 route update canary-release -f - <<'EOF'
{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream": {
"name": "new-version-v2",
"type": "roundrobin",
"nodes": {
"backend-v2:8080": 1
}
},
"weight": 5
},
{
"weight": 5
}
]
}
]
}
}
}
EOF

Common Patterns

A/B testing by query parameter

{
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [
["arg_variant", "==", "B"]
]
}
],
"weighted_upstreams": [
{
"upstream": {
"name": "variant-B",
"type": "roundrobin",
"nodes": {"variant-b:8080": 1}
}
}
]
}
]
}
}
}

Requests with ?variant=B → variant B backend.

Multi-rule routing (OR logic)

{
"plugins": {
"traffic-split": {
"rules": [
{
"match": [{"vars": [["http_x-api-id", "==", "1"]]}],
"weighted_upstreams": [
{"upstream": {"type": "roundrobin", "nodes": {"svc-a:8080": 1}}}
]
},
{
"match": [{"vars": [["http_x-api-id", "==", "2"]]}],
"weighted_upstreams": [
{"upstream": {"type": "roundrobin", "nodes": {"svc-b:8080": 1}}}
]
}
]
}
}
}

Using upstream_id for health checks

{
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": "canary-upstream",
"weight": 2
},
{
"weight": 8
}
]
}
]
}
}
}

Pre-create the upstream with a6 upstream create to configure health checks, retries, and other advanced settings.

Multi-condition AND match

{
"match": [
{
"vars": [
["arg_name", "==", "jack"],
["http_user-id", ">", "23"],
["http_x-env", "~~", "^(staging|canary)$"]
]
}
]
}

All three conditions must be true (AND logic within a single vars array).

Match Logic Reference

StructureLogic
Multiple entries in one vars arrayAND — all must match
Multiple objects in match arrayOR — any can match
Empty match or no matchUnconditional — always applies weights

Troubleshooting

SymptomCauseFix
Traffic ratio inaccurateRound-robin algorithm causes slight deviationExpected behavior; ratios converge over many requests
Match rule not triggeringVariable name wrong or operator mismatchUse http_header-name for headers, arg_name for query params
Health checks not workingInline upstream doesn't support checksUse upstream_id referencing a pre-created upstream with health checks
All traffic going to defaultMatch conditions never trueDebug with a6 route get and verify header/param names
Weight 0 not blocking trafficWeight 0 means "never forward" to that upstreamCorrect — set weight to 0 to exclude an upstream

Config Sync Example

version: "1"
routes:
- id: canary-api
uri: /api/*
plugins:
traffic-split:
rules:
- weighted_upstreams:
- upstream_id: canary-upstream
weight: 2
- weight: 8
upstream_id: stable-upstream
upstreams:
- id: stable-upstream
type: roundrobin
nodes:
"stable-backend:8080": 1
- id: canary-upstream
type: roundrobin
nodes:
"canary-backend:8080": 1

This page is generated from a6-plugin-traffic-split/SKILL.md in the api7/a6 repository. Browse all skills on the AI Agent Skills page.