Istio can act as a robust API Gateway, but it’s not just a simple proxy; it fundamentally reshapes how your services are exposed by injecting its control plane into the traffic flow.

Let’s see Istio in action as an API Gateway. Imagine a simple scenario: you have a product-service that exposes a REST API. We want to make this accessible externally, with some basic routing and security.

First, ensure you have Istio installed, typically with the istio-ingressgateway component enabled. This gateway is the entry point for external traffic.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: product-gateway
spec:
  selector:
    istio: ingressgateway # Use the default Istio ingress gateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*" # Or specify your domain, e.g., "api.example.com"

This Gateway resource tells the istio-ingressgateway to listen on port 80 for incoming HTTP traffic. Now, we need to define how to route that traffic to our product-service. This is where VirtualService comes in.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: product-virtualservice
spec:
  hosts:
  - "*" # Matches the host in the Gateway
  gateways:
  - product-gateway # Links to the Gateway resource
  http:
  - match:
    - uri:
        prefix: /products
    route:
    - destination:
        host: product-service.default.svc.cluster.local # The Kubernetes service name
        port:
          number: 8080 # The port your product-service listens on

When a request hits the istio-ingressgateway for /products, this VirtualService directs it to the product-service running in the default namespace on port 8080. The host field in destination is crucial; it’s the Kubernetes service name that Istio will resolve.

The real power comes from Istio’s ability to layer on advanced features. For example, let’s add a simple request header transformation:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: product-virtualservice
spec:
  hosts:
  - "*"
  gateways:
  - product-gateway
  http:
  - match:
    - uri:
        prefix: /products
    rewrite:
      uri: / # Rewrites the URI before sending to the service
    route:
    - destination:
        host: product-service.default.svc.cluster.local
        port:
          number: 8080
    headers:
      request:
        add:
          X-Forwarded-Proto: "https" # Adds a header

Here, we’ve added a rewrite rule to strip the /products prefix before sending the request to the product-service, making it appear as if the service directly handles /. We also added a custom X-Forwarded-Proto header. This demonstrates how Istio can modify requests before they reach your backend service, abstracting away external concerns.

Consider rate limiting. You can enforce limits directly at the gateway level:

apiVersion: networking.istio.io/v1alpha3
kind: RateLimitConfig
metadata:
  name: product-ratelimit
spec:
  # ... (Rate limit configuration details, e.g., limits per second, per minute)

You would then associate this RateLimitConfig with your VirtualService or Gateway to apply the limits. Istio handles the distributed enforcement, ensuring your services aren’t overwhelmed.

The mental model is that the istio-ingressgateway is a highly configurable Envoy proxy managed by Istio. Your Gateway and VirtualService resources are Istio’s way of defining the Envoy configuration – the routing rules, the TLS settings, the traffic management policies. Istio’s control plane (istiod) continuously watches these custom resources and pushes the corresponding Envoy configuration to the gateway pods. External requests enter the cluster through the istio-ingressgateway, which acts as the single pane of glass, enforcing policies and directing traffic to the appropriate internal services.

What most people don’t realize is how deeply Istio’s traffic management extends to fine-grained control over request routing based on headers, query parameters, or even weights for canary deployments, all without modifying application code. You can split traffic between two versions of your product-service like this:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: product-virtualservice
spec:
  hosts:
  - "*"
  gateways:
  - product-gateway
  http:
  - match:
    - uri:
        prefix: /products
    route:
    - destination:
        host: product-service.default.svc.cluster.local
        port:
          number: 8080
      weight: 90 # 90% of traffic
    - destination:
        host: product-service-v2.default.svc.cluster.local # A new version
        port:
          number: 8080
      weight: 10 # 10% of traffic

This allows you to roll out new versions of your product-service gradually and safely, routing a small percentage of traffic to product-service-v2 and observing its behavior before a full rollout.

The next step often involves securing this external access with TLS termination at the gateway, which requires configuring TLS certificates within the Gateway resource.

Want structured learning?

Take the full Istio course →