The options to deploy an egress gateway vary based on the traffic management API that you want to use.

Kubernetes Gateway API

To use the Kubernetes Gateway API custom resources to configure traffic management in your service mesh, you can deploy Gateway resources that expose your services. For more information about using the Gateway API in Istio, see this blog post.

To use the Kubernetes Gateway API, you can follow the Egress gateways guide in the community Istio docs. Be sure to use the Gateway API resources option in the steps.

Istio networking API

To use the classic Istio networking API to configure traffic management in your service mesh, you can deploy an Istio egress gateway by using Helm.

Before you begin

  1. Set up a sidecar mesh by using the Gloo Operator or Helm.
  2. Add services to the sidecar mesh. The guide walks you through an example for how to install the Bookinfo app and add it to your service mesh.

Step 1: Create an egress gateway

Use the Istio networking API to configure an Istio egress gateway by using Helm.

  1. If you have not already, set the following environment variables.

    1. Save the Solo distribution of Istio patch version and tag that you installed.
        export ISTIO_VERSION=1.26.2
      # Change the tags as needed
      export ISTIO_IMAGE=${ISTIO_VERSION}-solo
        
    2. Save the repo key for the minor version of the Solo distribution of Istio that you installed. This is the 12-character hash at the end of the repo URL us-docker.pkg.dev/gloo-mesh/istio-<repo-key>, which you can find in the Istio images built by Solo.io support article.
        # 12-character hash at the end of the minor version repo URL
      export REPO_KEY=<repo_key>
      export REPO=us-docker.pkg.dev/gloo-mesh/istio-${REPO_KEY}
      export HELM_REPO=us-docker.pkg.dev/gloo-mesh/istio-helm-${REPO_KEY}
        
    3. Get the revision that you used for your installation. Typically, this is main for a Helm installation, or gloo for a Gloo Operator installation.
        export REVISION=$(kubectl get pod -l app=istiod -n istio-system -o jsonpath='{.items[0].metadata.labels.istio\.io/rev}')      
      echo ${REVISION}
        
    4. Save the name and kubeconfig context of a cluster where you want to install the egress gateway. In a multicluster setup, this can be any workload cluster where you installed a service mesh.
        export CLUSTER_NAME=<cluster-name>
      export CLUSTER_CONTEXT=<cluster-context>
        
  2. Prepare a Helm values file for the Istio egress gateway. This sample command downloads an example file, egress-gateway.yaml, and updates the environment variables with the values that you previously set. You can further edit the file to provide your own details for production-level settings.

      curl -0L https://raw.githubusercontent.com/solo-io/gloo-mesh-use-cases/main/gloo-mesh-enterprise/istio-install/manual-helm/egress-gateway.yaml > egress-gateway.yaml
    envsubst < egress-gateway.yaml > egress-gateway-values.yaml
      
  3. Create the egress gateway.

      helm upgrade --install istio-egressgateway oci://${HELM_REPO}/gateway \
      --version ${ISTIO_VERSION} \
      --namespace istio-egress \
      --create-namespace \
      --kube-context ${CLUSTER_CONTEXT} \
      --wait \
      -f egress-gateway-values.yaml
      
  4. Verify that your egress gateway pod is running.

      kubectl get pods -n istio-egress
      

    Example output:

      NAME                                   READY   STATUS    RESTARTS   AGE
    istio-egressgateway-5bfbdb5958-9svmt   1/1     Running   0          25s
      

Step 2: Route traffic through the egress gateway

Create ServiceEntry, Gateway, and DestinationRule resources to enable routing through the egress gateway to the httpbin.org host. Then, create a VirtualService to configure sidecars in the mesh to always route egress traffic to the host through the egress gateway.

  1. Create a ServiceEntry that represents the external service httpbin.org.

      kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: ServiceEntry
    metadata:
      name: httpbin.org
      namespace: istio-egress
    spec:
      hosts:
      - httpbin.org
      ports:
      - name: http
        number: 80
        protocol: HTTP
      resolution: DNS
    EOF
      
  2. Send a request from the productpage app to httpbin.org. This request succeeds because the ServiceEntry enables outgoing requests to httpbin.org without the use of an egress gateway.

      kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/get" 
      

    Example output:

      ...
    < HTTP/1.1 200 OK
    < date: Thu, 05 Jun 2025 20:58:37 GMT
    < content-type: application/json
    < content-length: 941
    < server: envoy
    < access-control-allow-origin: *
    < access-control-allow-credentials: true
    < x-envoy-upstream-service-time: 769
    ...
      
  3. Create dedicated Gateway and DestinationRule resources that route all outgoing traffic requests to httpbin.org on port 80.

      kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: Gateway
    metadata:
      name: istio-egressgateway
      namespace: istio-egress
    spec:
      selector:
        istio: egressgateway
      servers:
      - port:
          number: 80
          name: http
          protocol: HTTP
        hosts:
        - httpbin.org
    ---
    apiVersion: networking.istio.io/v1
    kind: DestinationRule
    metadata:
      name: egressgateway-for-httpbin.org
      namespace: istio-egress
    spec:
      host: istio-egressgateway.istio-egress.svc.cluster.local
      subsets:
      - name: httpbin.org
    EOF
      
  4. Create a VirtualService to configure sidecars in the mesh to always route egress traffic to httpbin.org through the egress gateway.

      kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: VirtualService
    metadata:
      name: direct-httpbin.org-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - httpbin.org
      gateways:
      - istio-egressgateway
      - mesh
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: httpbin.org
            port:
              number: 80
          weight: 100
      - match:
        - gateways:
          - istio-egressgateway
          port: 80
        route:
        - destination:
            host: httpbin.org
            port:
              number: 80
          weight: 100
    EOF
      
  5. Repeat the request from the productpage app to httpbin.org. Verify that your response contains the X-Envoy-Peer-Metadata-Id header with a value similar to router~10.XX.XX.XX~istio-egressgateway-5bfbdb5958-9svmt.istio-egress~istio-egress.svc.cluster.local, which verifies that traffic is routed through the egress gateway.

      kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/get" 
      

    Example output:

      ...
           "X-Envoy-Peer-Metadata": "ChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwqXAQoGTEFCRUxTEowBKokBChwKA2FwcBIVGhNpc3Rpby1lZ3Jlc3NnYXRld2F5CjgKH3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLW5hbWUSFRoTaXN0aW8tZWdyZXNzZ2F0ZXdheQovCiNzZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1yZXZpc2lvbhIIGgYxLjI2LjAKLgoETkFNRRImGiRpc3Rpby1lZ3Jlc3NnYXRld2F5LTViZmJkYjU5NTgtOXN2bXQKGwoJTkFNRVNQQUNFEg4aDGlzdGlvLWVncmVzcwpcCgVPV05FUhJTGlFrdWJlcm5ldGVzOi8vYXBpcy9hcHBzL3YxL25hbWVzcGFjZXMvaXN0aW8tZWdyZXNzL2RlcGxveW1lbnRzL2lzdGlvLWVncmVzc2dhdGV3YXkKJgoNV09SS0xPQURfTkFNRRIVGhNpc3Rpby1lZ3Jlc3NnYXRld2F5", 
        "X-Envoy-Peer-Metadata-Id": "router~10.0.79.208~istio-egressgateway-5bfbdb5958-9svmt.istio-egress~istio-egress.svc.cluster.local"
      }, 
      "origin": "10.0.XX.XX, 35.169.103.7", 
      "url": "http://httpbin.org/get"
    }
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 200 OK
    < date: Thu, 05 Jun 2025 21:02:24 GMT
    < content-type: application/json
    < content-length: 1045
    < server: envoy
    < access-control-allow-origin: *
    < access-control-allow-credentials: true
    < x-envoy-upstream-service-time: 3230
    ...
      
  6. Optional: If you enabled Istio access logs, you can review the logs of the egress gateway and verify that you can see a log entry for the request from productpage to httpbin.org through the egress gateway.

      kubectl logs -l istio=egressgateway -c istio-proxy -n istio-egress | tail
      

    Example output:

      {"authority":"httpbin.org","bytes_received":0,"bytes_sent":1045,"connection_termination_details":null,"downstream_local_address":"10.0.XX.XX:80","downstream_remote_address":"10.0.XX.XX:34760","duration":3226,"method":"GET","path":"/get","protocol":"HTTP/2","request_id":"c75481d6-a2df-4815-9a7b-7b94deba2097","requested_server_name":null,"response_code":200,"response_code_details":"via_upstream","response_flags":"-","route_name":null,"start_time":"2025-06-05T21:02:20.806Z","upstream_cluster":"outbound|80||httpbin.org","upstream_host":"52.202.28.30:80","upstream_local_address":"10.0.XX.XX:57306","upstream_service_time":"3226","upstream_transport_failure_reason":null,"user_agent":"curl/7.83.1-DEV","x_forwarded_for":"10.0.XX.XX"}
      

Step 3: Apply an egress policy

Now that all traffic to the httpbin.org service is routed through the egress gateway, you can apply additional routing and security policies to it.

AuthorizationPolicy

In this example, you learn how to use an Istio AuthorizationPolicy to restrict access to the external service for specific HTTP methods.

  1. Create an AuthorizationPolicy that allows only GET requests to the httpbin.org service. You apply this policy to the egress gateway by using the targetRefs section.

      kubectl apply -f - <<EOF
    apiVersion: security.istio.io/v1
    kind: AuthorizationPolicy
    metadata:
      name: httpbin
      namespace: istio-egress
    spec:
      targetRefs:
      - kind: Gateway
        name: egress-waypoint
        group: gateway.networking.k8s.io
      action: ALLOW
      rules:
      - to:
        - operation:
            hosts: ["httpbin.org"]
            methods: ["GET"]
            paths: ["/get"]
    EOF
      
  2. Send a POST request to httpbin.org and verify that this request is denied with a 405 HTTP response code.

      kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/post" 
      

    Example output:

      ...
    HTTP/1.1 405 Method Not Allowed
    date: Thu, 05 Jun 2025 21:08:43 GMT
    content-type: text/html
    content-length: 178
    server: envoy
    allow: OPTIONS, POST
    access-control-allow-origin: *
    access-control-allow-credentials: true
    x-envoy-upstream-service-time: 1465
      
  3. Repeat the request. This time, you use the GET HTTP method that is allowed by the AuthorizationPolicy. Verify that your request now succeeds.

      kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/get" 
      

    Example output:

      ...
    < HTTP/1.1 200 OK
    < date: Thu, 05 Jun 2025 20:58:37 GMT
    < content-type: application/json
    < content-length: 941
    < server: envoy
    < access-control-allow-origin: *
    < access-control-allow-credentials: true
    < x-envoy-upstream-service-time: 769
    ...
      

Header manipulation

In this example, you explore how to add headers to a request by using an Istio VirtualService.

  1. Update the VirtualService to add the my-added-header: added-value header to your request.

      kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: VirtualService
    metadata:
      name: direct-httpbin.org-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - httpbin.org
      gateways:
      - istio-egressgateway
      - mesh
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: httpbin.org
            port:
              number: 80
          weight: 100
      - match:
        - gateways:
          - istio-egressgateway
          port: 80
        route:
        - destination:
            host: httpbin.org
            port:
              number: 80
          weight: 100
        headers:
          request:
            add:
              my-added-header: added-value
    EOF
      
  2. Send a request to the httpbin.org external service and verify that you see the added request header.

      kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/get" 
      

    Example output:

       < HTTP/1.1 200 OK
       < date: Thu, 05 Jun 2025 21:10:41 GMT
       < content-type: application/json
       < content-length: 1084
       < server: envoy
       < access-control-allow-origin: *
       < access-control-allow-credentials: true
       < x-envoy-upstream-service-time: 2533
       < 
       { [1084 bytes data]
       100  1084  100  1084    0     0    42HTTP/1.1 200 OK02  0:00:02 --:--:--   427
       date: Thu, 05 Jun 2025 21:10:41 GMT
       content-type: application/json
       content-length: 1084
       server: envoy
       access-control-allow-origin: *
       access-control-allow-credentials: true
       x-envoy-upstream-service-time: 2533
    
       {
         "args": {}, 
         "headers": {
           "Accept": "*/*", 
           "Host": "httpbin.org", 
           "My-Added-Header": "added-value", 
       ...
       

TLS origination

By default, all traffic within your service mesh is secured via mutual TLS. However, when routing traffic through an egress gateway, the egress gateway terminates the TLS connection and forwards the unencrypted request to the external service.

With TLS origination, you instruct the egress gateway to re-encrypt the request before it is forwarded to the external service.

  1. Add targetPort: 443 to your ServiceEntry to instruct the egress gateway to use port 443 when routing traffic to httpbin.org.

      kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: ServiceEntry
    metadata:
      name: httpbin.org
      namespace: istio-egress
    spec:
      hosts:
      - httpbin.org
      ports:
      - number: 80
        name: http
        protocol: HTTP
        targetPort: 443 
      resolution: DNS
    EOF
      
  2. Create a DestinationRule to instruct the egress gateway to originate a TLS connection when routing traffic to httpbin.org.

      kubectl apply -f- <<EOF
    apiVersion: networking.istio.io/v1
    kind: DestinationRule
    metadata:
      name: httpbin.org-tls
      namespace: istio-egress
    spec:
      host: httpbin.org
      trafficPolicy:
        tls:
          mode: SIMPLE
    EOF
      
  3. Send a request to the httpbin.org external service and verify that traffic is sent to https://httpbin.org/get.

      kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/get" 
      

    Example output:

       ...
       {
         "args": {}, 
         "headers": {
           "Accept": "*/*", 
           "Host": "httpbin.org", 
           "My-Added-Header": "added-value", 
           "User-Agent": "curl/7.83.1-DEV", 
           "X-Amzn-Trace-Id": "Root=1-68420852-036726fc21bb8eb76838deff", 
           "X-Envoy-Attempt-Count": "1", 
           "X-Envoy-External-Address": "10.0.XX.XX", 
           "X-Envoy-Peer-Metadata": "ChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwqXAQoGTEFCRUxTEowBKokBChwKA2FwcBIVGhNpc3Rpby1lZ3Jlc3NnYXRld2F5CjgKH3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLW5hbWUSFRoTaXN0aW8tZWdyZXNzZ2F0ZXdheQovCiNzZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1yZXZpc2lvbhIIGgYxLjI2LjAKLgoETkFNRRImGiRpc3Rpby1lZ3Jlc3NnYXRld2F5LTViZmJkYjU5NTgtOXN2bXQKGwoJTkFNRVNQQUNFEg4aDGlzdGlvLWVncmVzcwpcCgVPV05FUhJTGlFrdWJlcm5ldGVzOi8vYXBpcy9hcHBzL3YxL25hbWVzcGFjZXMvaXN0aW8tZWdyZXNzL2RlcGxveW1lbnRzL2lzdGlvLWVncmVzc2dhdGV3YXkKJgoNV09SS0xPQURfTkFNRRIVGhNpc3Rpby1lZ3Jlc3NnYXRld2F5", 
           "X-Envoy-Peer-Metadata-Id": "router~10.0.79.208~istio-egressgateway-5bfbdb5958-9svmt.istio-egress~istio-egress.svc.cluster.local"
         }, 
         "origin": "10.0.XX.XX, 35.169.103.7", 
         "url": "https://httpbin.org/get"
       }
       

Cleanup

You can optionally remove the resources that you created in this guide.

  • Policies:
      kubectl delete destinationrule httpbin.org-tls -n istio-egress
    kubectl delete authorizationpolicy httpbin -n istio-egress
      
  • Gateway and routing resources:
      kubectl delete virtualservice direct-httpbin.org-through-egress-gateway -n istio-egress
    kubectl delete destinationrule egressgateway-for-httpbin.org -n istio-egress
    kubectl delete gateway.networking.istio.io/istio-egressgateway -n istio-egress
    kubectl delete serviceentry httpbin.org -n istio-egress
      
  • Istio egress gateway:
      helm uninstall istio-egressgateway -n istio-egress
    kubectl delete namespace istio-egress
      

Next

  • Launch the Gloo UI to review the Istio insights that were captured for your service mesh setup. Gloo Mesh comes with an insights engine that automatically analyzes your Istio setups for health issues. These issues are displayed in the UI along with recommendations to harden your Istio setups. The insights give you a checklist to address issues that might otherwise be hard to detect across your environment. For more information, see Insights.
  • When it’s time to upgrade your service mesh, you can perform a safe in-place upgrade by using the Gloo Operator or Helm.