Gateway Selection Report
Overview
This report evaluates 8 API gateway candidates for the BANA POS system. The gateway serves as the single entry point for all external traffic, routing requests to the correct backend microservice while providing resilience and observability.
Routing Strategy: Service-Owned Prefix — each service sets APP_ENV_SERVER_BASE_PATH=/v1/api/<service>. Docker label/annotation self-registration — each service declares its own route. Zero central route config.
Architecture: Host-level Nginx (TLS + domains) → Traefik (Docker, :30080, service routing + resilience + observability) → Backend Services.
Candidates
| # | Gateway | Language | License | First Release | GitHub Stars |
|---|---|---|---|---|---|
| 1 | Nginx (OSS) | C | BSD-2 | 2004 | 26k+ |
| 2 | HAProxy | C | GPL-2 | 2001 | 5k+ |
| 3 | Traefik | Go | MIT | 2015 | 53k+ |
| 4 | Caddy | Go | Apache-2 | 2015 | 60k+ |
| 5 | Envoy | C++ | Apache-2 | 2016 | 25k+ |
| 6 | Kong (OSS) | Lua/Nginx | Apache-2 | 2015 | 40k+ |
| 7 | Apache APISIX | Lua/Nginx | Apache-2 | 2019 | 14k+ |
| 8 | KrakenD (CE) | Go | Apache-2 | 2017 | 2k+ |
Performance Benchmarks
Based on independent K8s Ingress Controller tests:
| Gateway | RPS | P99 Latency | CPU Usage | Memory | Errors Under Stress |
|---|---|---|---|---|---|
| HAProxy | ~42,000 | Lowest | ~50% | ~50MB | 0 |
| Nginx | ~15,200 | Low | Medium | ~80MB | Low |
| Traefik | ~19,000 | Medium | Stable | ~100MB | 1,342 |
| Envoy | ~18,500 | Low-Medium | ~73% | ~150MB | Low |
| KrakenD | ~70,000* | Lowest | 4-6% | ~30MB | N/A |
| Kong | ~15,000 | High (1032ms avg) | High | ~200MB+ | Medium |
| APISIX | ~18,000 | Medium (1025ms avg) | Medium | ~100MB | Low |
| Caddy | ~12,000 | Medium | Medium | ~60MB | Low |
*KrakenD 70k RPS is their own benchmark (stateless, no K8s overhead). Take with grain of salt vs independent tests.
Feature Matrix
| Feature | Nginx OSS | HAProxy | Traefik | Caddy | Envoy | Kong OSS | APISIX | KrakenD CE |
|---|---|---|---|---|---|---|---|---|
| Docker Label Discovery | No | 3rd-party | Native | No | No | No | No | No |
| K8s Ingress Controller | Yes (EOL Mar 2026!) | Yes | Yes | WIP | Yes | Yes | Yes | Yes |
| K8s Gateway API | No | Yes | Yes (native) | No | Yes (native) | Yes | Yes | No |
| Circuit Breaker | No | No | Yes | No | Yes | Plugin | Plugin | Yes |
| Rate Limiting | 3rd-party | Basic | Yes | No | Yes | Plugin | Plugin | Yes |
| Active Health Checks | No (paid) | Yes | Yes | No | Yes | Yes | Yes | No |
| Retry/Timeout | Limited | Yes | Yes | No | Yes (advanced) | Plugin | Plugin | Yes |
| WebSocket | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Enterprise only! |
| gRPC | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| HTTP/3 (QUIC) | Yes | Yes | Yes (exp.) | Yes | Yes | No | Yes | No |
| Auto TLS (ACME) | No | No | Yes | Yes (best) | No | No | No | No |
| Built-in Metrics | No | Stats | Yes (Prometheus) | Yes | Yes | Plugin | Plugin | Yes |
| Dashboard/UI | No | Stats page | Yes (built-in) | No | Admin API | Paid | Free | No |
| Security Headers | Manual | Manual | Yes (middleware) | Manual | Yes (filter) | Plugin | Plugin | Yes |
| Hot Reload | nginx -s reload | reload | Automatic | Automatic | xDS API | DB-driven | etcd/YAML | File reload |
| Stateless (no DB) | Yes | Yes | Yes | Yes | Yes | No (PostgreSQL) | Needs etcd | Yes |
| Config Complexity | Medium | Medium | Low (labels/YAML) | Low | High | Medium | Medium | Low |
| Learning Curve | Low | Medium | Low | Very Low | High | Medium | Medium | Low |
Critical Disqualifiers for BANA
| Candidate | Disqualifier | Severity |
|---|---|---|
| Nginx OSS | K8s Ingress Controller EOL March 2026. No circuit breaker, no active health checks, no rate limiting, no metrics in OSS. No Docker label discovery. | CRITICAL |
| Caddy | K8s Ingress Controller is WIP/immature. No circuit breaker, no rate limiting, no active health checks. No Docker label discovery. | HIGH |
| KrakenD CE | WebSocket support is Enterprise-only (paid). Required for signal service. No Docker label discovery. No K8s Gateway API support. | CRITICAL |
| Kong OSS | Requires PostgreSQL database (operational overhead). Heaviest resource footprint (~200MB+). Plugin-dependent for most features. | MEDIUM |
| Apache APISIX | Requires etcd cluster (or standalone YAML mode). No Docker label discovery. More complex setup than needed. | MEDIUM |
| Envoy | Highest config complexity (xDS, YAML clusters/listeners). No Docker label discovery. Designed for service mesh, overkill for gateway-only use. | MEDIUM |
Finalist Analysis
After eliminating candidates with critical/high disqualifiers, three strong candidates remain:
Traefik
Strengths:
- Native Docker label discovery (exactly matches service-owned prefix pattern)
- Native K8s Gateway API support (future-proof for K8s migration)
- Built-in circuit breaker, rate limiting, retry, health checks (no plugins needed)
- Built-in Prometheus metrics + dashboard
- Auto TLS with Let's Encrypt
- WebSocket native support
- Lowest config complexity for Docker-first workflow
- MIT license, huge community (53k stars)
- CNCF graduated project
Weaknesses:
- Not the fastest (19k RPS vs HAProxy's 42k)
- 1,342 errors under extreme stress test
- No advanced L4 load balancing features
HAProxy
Strengths:
- Absolute best raw performance (42k RPS, zero errors)
- Lowest latency at P99
- Lowest CPU/memory footprint
- K8s Gateway API support
- Advanced L4/L7 load balancing (sticky sessions, connection draining)
- 24 years of battle-tested stability
- Active health checks, retry
Weaknesses:
- No native Docker label discovery (requires 3rd-party EasyHAProxy)
- No built-in circuit breaker
- No auto TLS (ACME)
- No built-in Prometheus metrics endpoint (needs exporter)
- No built-in dashboard (just stats page)
- Config files are more verbose
- EasyHAProxy is a community project, not HAProxy official
Envoy
Strengths:
- Most powerful feature set overall
- Real-time config via xDS API (zero-downtime changes)
- Powers Istio, Consul Connect, AWS App Mesh
- Best for service mesh if you go that route
- Native K8s Gateway API (Envoy Gateway)
- Advanced circuit breaking, outlier detection, retry
Weaknesses:
- Highest config complexity (steep learning curve)
- Highest memory usage (~150MB)
- No Docker label discovery
- Overkill if you don't need service mesh
- Requires significant Envoy expertise
Scoring Matrix
Weighted scoring (1-5, higher = better for BANA):
| Criteria (Weight) | Traefik | HAProxy | Envoy |
|---|---|---|---|
| Docker-first workflow (25%) | 5 | 2 | 1 |
| K8s Gateway API ready (20%) | 5 | 4 | 5 |
| Built-in resilience (15%) | 5 | 3 | 5 |
| Performance/efficiency (15%) | 3 | 5 | 4 |
| Operational simplicity (15%) | 5 | 3 | 1 |
| Observability built-in (10%) | 5 | 2 | 5 |
| Weighted Total | 4.60 | 3.15 | 3.10 |
Migration Path Comparison
| Phase | Traefik | HAProxy | Envoy |
|---|---|---|---|
| Docker Compose (now) | Labels → auto-discover | EasyHAProxy labels or manual cfg | Manual envoy.yaml per service |
| K8s Migration | Labels → IngressRoute CRDs (mechanical) | Labels → annotations (OK) | YAML → Gateway API CRDs (complex) |
| Service Mesh (future) | Traefik Mesh (optional) | No mesh story | Istio/Consul (native) |
Decision
Selected: Traefik v3.6 (score 4.60/5)
Traefik is the clear winner for BANA because:
- Docker label self-registration is a native feature — services declare their own routes via Docker labels, achieving zero central route configuration
- K8s Gateway API is natively supported — when migrating from Docker Compose to Kubernetes, the conversion from labels to IngressRoute CRDs is mechanical
- All resilience features built-in — circuit breaker, rate limiting, retry, health checks work out of the box without plugins or external dependencies
- Prometheus metrics and dashboard are built-in — no additional exporters or sidecars needed
- Operational simplicity — the entire gateway configuration fits in a single
traefik.ymlfile plus Docker labels on each service
The performance gap (19k vs 42k RPS for HAProxy) is acceptable at BANA's current scale. If performance becomes a bottleneck, HAProxy can be considered as a future L4 load balancer in front of Traefik.
Related Pages
| Document | Description |
|---|---|
| Gateway Overview | Identity card + service catalog |
| Architecture | C4 views, request-routing flows |
| Routing | Docker label routing, 3 routing patterns |
| ADR-0001 | Traefik label-based dynamic routing decision |
| Decisions | All ADRs |