Ingress
Multi-tenant ingress and routing service
Location: go/apps/ingress/
CLI Command: unkey run ingress
What It Does
Ingress is the multi-tenant HTTP ingress service that serves as the entry point for all customer traffic. It handles TLS termination, hostname-based routing, and proxying requests to environment-scoped gateways.
Ingress handles four main responsibilities:
- TLS Termination: Terminates TLS for custom domains using ACME (Let's Encrypt) or local certificates
- Hostname Routing: Looks up ingress routes by hostname to find the target deployment and environment
- Gateway Selection: Routes requests to the appropriate environment-scoped gateway
- Smart Proxying: Forwards to local gateways or cross-region NLBs with hop count protection
Architecture
Multi-Tenant Design
Ingress is a multi-tenant service running as a Kubernetes Deployment behind a Network Load Balancer. A single Ingress instance handles traffic for all customer deployments, performing hostname lookups and routing decisions to forward requests to the appropriate environment gateway.
Request Flow
Database Schema
Ingress uses the following tables for routing decisions:
TLS Strategy
ACME (Let's Encrypt)
Ingress uses ACME (Automated Certificate Management Environment) to obtain and renew TLS certificates from Let's Encrypt:
- Challenge Handler: The
/acmeroute responds to HTTP-01 challenges - Certificate Storage: Certificates are stored in MySQL
certificatestable - Automatic Renewal: Certificates are renewed before expiration
- SNI Support: Multiple hostnames supported via Server Name Indication
Local Development
For local development, Ingress can generate self-signed certificates:
TLS Termination Flow
- Client connects with SNI hostname (e.g.,
api.customer.com) - Ingress looks up certificate in database by hostname
- TLS handshake completes with customer's certificate
- Request is decrypted and forwarded to environment gateway as plain HTTP
Gateway Routing
Ingress routes requests to environment-scoped gateways based on the environment_id from the ingress route lookup.
Gateway Discovery
Routing Strategy
Ingress routes based on:
environment_idfrom the ingress route lookup- Gateway service discovery via Kubernetes DNS
- Current region (for local vs cross-region routing)
If no local gateway is found, Ingress forwards to the region's NLB, triggering cross-region forwarding.
Request Headers
Ingress passes metadata to the Gateway via HTTP headers:
X-Deployment-ID: The target deployment IDX-Environment-ID: The environment ID (for validation)X-Unkey-Hop-Count: Hop count for cross-region loop prevention
Cross-Region Forwarding
When no local gateway is available, Ingress forwards requests to the region's Network Load Balancer.
Hop Count Protection
To prevent infinite loops, Ingress tracks hops via HTTP header:
Forwarding Strategy
Why NLB Instead of Direct Gateway?
Forwarding to the region's NLB (instead of directly to a remote gateway) provides:
- Load balancing: NLB distributes across available Ingress instances in the target region
- Health checking: NLB only routes to healthy Ingress replicas
- Simplicity: No need for cross-region gateway discovery
- Consistency: Same ingress lookup and routing logic applies in the target region
Error Handling
Ingress provides user-friendly error pages for common scenarios:
Error Middleware
Status Code Mapping
- 404 Not Found: No ingress route configured for hostname
- 503 Service Unavailable: Environment gateway not available
- 508 Loop Detected: Maximum hop count exceeded (prevents infinite loops)
Observability
Ingress uses structured logging for:
- Hostname lookups and routing decisions
- Gateway discovery and selection
- Proxy operations (success/failure)
- Cross-region forwarding
- TLS certificate operations
- Error conditions with context
Future Improvements
Planned Features
- Gateway Health Checks: Active health probing for environment gateways
- Sticky Sessions: Support sticky routing based on client session
Scalability
Ingress is designed to scale horizontally:
- Stateless: No persistent state, all routing from database
- Database-driven: Routing decisions from MySQL lookups (indexed on hostname)
- Minimal processing: Pure proxy layer, no business logic execution