Traffic Splitting
Canary releases, blue-green deployments, A/B testing, and shadow traffic.
Bastion supports traffic splitting for safe deployments and experimentation. Traffic policies are configured per-route and control how requests are distributed across target groups. Four strategies are available: canary, blue-green, A/B testing, and traffic mirroring.
Traffic Policy Structure
Each route can have a TrafficPolicy that defines the splitting strategy and rules:
type TrafficPolicy struct {
Type TrafficSplitType // canary, blueGreen, abTest, mirror, weighted
Rules []TrafficRule // Routing rules
MirrorTarget string // Target URL for mirror traffic
}
type TrafficRule struct {
Match TrafficMatch // How to match requests
TargetTags []string // Tags identifying which targets receive matched traffic
Weight int // Weight for percentage-based matching
}
type TrafficMatch struct {
Type TrafficMatchType // header, cookie, weight, ipRange
Key string // Header name or cookie name
Value string // Expected value
Negate bool // Invert the match
}Target Tags
Traffic splitting uses tags to identify target groups. Assign tags to targets when configuring routes:
bastion.WithRoute(bastion.RouteConfig{
Path: "/api/*",
Targets: []bastion.TargetConfig{
{URL: "http://v1-stable:8080", Weight: 1, Tags: []string{"stable"}},
{URL: "http://v2-canary:8080", Weight: 1, Tags: []string{"canary"}},
},
TrafficPolicy: &bastion.TrafficPolicy{
Type: bastion.TrafficCanary,
Rules: []bastion.TrafficRule{
{
Match: bastion.TrafficMatch{Type: bastion.MatchWeight},
TargetTags: []string{"canary"},
Weight: 10, // 10% to canary
},
},
},
})Canary Deployments
Canary deployments route a configurable percentage of traffic to a new version while the majority continues to hit the stable version.
TrafficPolicy: &bastion.TrafficPolicy{
Type: bastion.TrafficCanary,
Rules: []bastion.TrafficRule{
{
Match: bastion.TrafficMatch{Type: bastion.MatchWeight},
TargetTags: []string{"canary"},
Weight: 5, // 5% of traffic goes to canary targets
},
},
}How it works:
- For each request, a random number between 0-99 is generated.
- If the number is less than the
Weight, the request is routed to targets with the specified tags. - Otherwise, the request goes to non-canary targets (targets without the
"canary"tag).
Gradually increase the weight as confidence in the new version grows:
- 5% -- initial canary validation
- 25% -- broader testing
- 50% -- half-split comparison
- 100% -- full rollout (or remove the traffic policy)
Blue-Green Deployments
Blue-green deployments maintain two complete environments and switch traffic between them. The active version is determined by matching rules:
TrafficPolicy: &bastion.TrafficPolicy{
Type: bastion.TrafficBlueGreen,
Rules: []bastion.TrafficRule{
{
Match: bastion.TrafficMatch{Type: bastion.MatchHeader, Key: "X-Version", Value: "green"},
TargetTags: []string{"green"},
},
},
}With targets:
Targets: []bastion.TargetConfig{
{URL: "http://blue-v1:8080", Tags: []string{"blue"}},
{URL: "http://green-v2:8080", Tags: []string{"green"}},
}Requests with X-Version: green go to the green environment. All other requests go to the blue environment. To switch, update the default routing or swap the tags.
A/B Testing
A/B testing routes requests based on header or cookie values, enabling experiments where different user segments see different implementations:
Header-Based Routing
TrafficPolicy: &bastion.TrafficPolicy{
Type: bastion.TrafficABTest,
Rules: []bastion.TrafficRule{
{
Match: bastion.TrafficMatch{Type: bastion.MatchHeader, Key: "X-Experiment", Value: "new-checkout"},
TargetTags: []string{"experiment-b"},
},
},
}Cookie-Based Routing
TrafficPolicy: &bastion.TrafficPolicy{
Type: bastion.TrafficABTest,
Rules: []bastion.TrafficRule{
{
Match: bastion.TrafficMatch{Type: bastion.MatchCookie, Key: "ab_group", Value: "B"},
TargetTags: []string{"variant-b"},
},
},
}Requests matching the rule go to the specified target tags. Unmatched requests go to the default (untagged or remaining) targets.
Negated Matching
Use Negate: true to invert a match:
Match: bastion.TrafficMatch{
Type: bastion.MatchHeader,
Key: "X-Internal",
Value: "true",
Negate: true, // Route to this group when the header is NOT "true"
}Traffic Mirroring (Shadow Traffic)
Traffic mirroring duplicates requests to a secondary target for testing without affecting the primary response. The client always receives the response from the primary targets -- the mirror response is discarded.
TrafficPolicy: &bastion.TrafficPolicy{
Type: bastion.TrafficMirror,
MirrorTarget: "http://shadow-v2:8080",
}How it works:
- The request is forwarded to the primary target as usual.
- A copy of the request is sent asynchronously to the
MirrorTarget. - The mirror response is discarded.
- The client receives the primary response without any added latency.
Use mirroring to validate a new version handles production traffic correctly before routing real users to it.
Weighted Traffic Distribution
For explicit weight-based distribution across multiple target groups:
TrafficPolicy: &bastion.TrafficPolicy{
Type: bastion.TrafficWeighted,
Rules: []bastion.TrafficRule{
{
Match: bastion.TrafficMatch{Type: bastion.MatchWeight},
TargetTags: []string{"v1"},
Weight: 70,
},
{
Match: bastion.TrafficMatch{Type: bastion.MatchWeight},
TargetTags: []string{"v2"},
Weight: 30,
},
},
}Match Types
| Type | Constant | Description |
|---|---|---|
| Header | bastion.MatchHeader | Match on request header value |
| Cookie | bastion.MatchCookie | Match on cookie value |
| Weight | bastion.MatchWeight | Random percentage-based matching |
| IP Range | bastion.MatchIPRange | Match on client IP range |
Global Traffic Split Toggle
Traffic splitting can be enabled or disabled globally:
bastion.WithConfig(bastion.Config{
TrafficSplit: bastion.TrafficSplitConfig{
Enabled: true,
},
})When Enabled is false, all traffic policies on routes are ignored and requests go to the full target list.