The most surprising thing about Istio Gateways is that they aren’t actually part of Istio itself in the way you might think; they’re just Envoy proxies configured by Istio’s control plane.

Let’s see this in action. Imagine we have a simple web service deployed in our Kubernetes cluster, and we want to expose it to the outside world via an Istio Gateway.

First, we need our application. Here’s a basic Deployment and Service for a fictional my-app:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: nginxdemos/hello:plain-text
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

Now, to expose this service externally, we need an Istio Gateway and a VirtualService. The Gateway resource tells Istio (and specifically, the Envoy proxy it manages) which ports to listen on and what TLS certificates to use. The VirtualService then routes incoming traffic based on hostnames and paths to our internal Kubernetes Service.

Here’s a Gateway that listens on port 80 for HTTP traffic and port 443 for HTTPS traffic:

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: my-gateway
spec:
  selector:
    istio: ingressgateway # This selects the default Istio ingress gateway deployment
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*" # Listen for any host
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: my-tls-secret # Kubernetes secret containing TLS cert and key
    hosts:
    - "myapp.example.com" # Listen specifically for this host

And here’s the VirtualService that routes traffic for myapp.example.com to our my-app-service:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: my-app-virtualservice
spec:
  hosts:
  - "myapp.example.com"
  gateways:
  - my-gateway # Link this VirtualService to our Gateway
  http:
  - route:
    - destination:
        host: my-app-service.default.svc.cluster.local # The internal Kubernetes service
        port:
          number: 80

To make the HTTPS part work, you’d need a Kubernetes Secret named my-tls-secret in the same namespace as your Gateway, containing your TLS certificate and private key. For example:

kubectl create secret tls my-tls-secret \
  --cert=path/to/your/tls.crt \
  --key=path/to/your/tls.key

With these resources applied, Istio’s control plane will configure the Envoy proxy running as the istio-ingressgateway (or whatever your Gateway’s selector points to) to listen on the specified ports. Your traffic will hit this Envoy proxy, which will then consult the VirtualService to determine where to send the request within your cluster.

The magic here is that the Gateway resource itself doesn’t do anything; it’s purely a configuration object. Istio’s istiod control plane watches for these Gateway resources and translates their specifications into the complex Envoy configuration that the actual ingress gateway proxy needs to function. This separation allows you to define network entry points independently from how traffic is routed internally.

The default Istio ingress gateway deployment is typically exposed via a Kubernetes Service of type LoadBalancer. This LoadBalancer service provisions an external IP address, which is the entry point for your external traffic into the cluster and then into Istio. You can find this IP with:

kubectl get svc istio-ingressgateway -n istio-system

The most common pitfall is forgetting that the Gateway resource only declares an intent to listen on ports and use certificates; it’s the istio-ingressgateway deployment’s Kubernetes Service (usually of type LoadBalancer) that actually provides the external IP and network reachability.

Once you have external traffic hitting your gateway, the next logical step is to implement more granular routing rules, like traffic splitting for canary deployments, using multiple VirtualService resources.

Want structured learning?

Take the full Istio course →