---
title: "Envoy Gateway on Kubernetes: Installation, Routing, and Ingress Migration | Hostman Docs"
description: "Learn how to install Envoy Gateway on Kubernetes, configure GatewayClass, Gateway, and HTTPRoute to route HTTP traffic, and migrate from Nginx Ingress using ingress2gateway."
---

> For the complete documentation index for AI agents, see [llms.txt](https://hostman.com/llms.txt).

Envoy Gateway is a Kubernetes Gateway API controller that uses Envoy Proxy to handle incoming traffic. You can use it to publish HTTP, HTTPS, gRPC, TCP, and UDP services, configure routing by domain and path, manage TLS, and apply traffic policies using standard Kubernetes resources.

Gateway API is the evolution of Ingress. Instead of a single Ingress resource, it splits responsibilities across several dedicated objects:

-   `GatewayClass` defines which controller will manage the gateways.
-   `Gateway` describes the entry point: ports, protocols, and TLS settings.
-   `HTTPRoute` defines HTTP traffic routing rules to backend services.
-   `GRPCRoute`, `TCPRoute`, `UDPRoute`, `TLSRoute` handle other traffic types.

Envoy Gateway watches these resources and uses them to create an Envoy Proxy that accepts external traffic and routes it to services inside the cluster.

## Installation

To install Envoy Gateway:

1.  Go to the **Kubernetes** section and click on the cluster.
2.  Navigate to the **Addons** tab and select **Envoy Gateway**.
3.  (Optional) Enable **Advanced setup** to edit `values.yaml`. For most use cases, the defaults are a good starting point.
4.  Click **Install**.
5.  Wait for the process to complete and verify that the Envoy Gateway pods are running:

```shell
kubectl get pods -n envoy-gateway-system
```

All pods should show a `Running` status.

You can also confirm that the Gateway API resources are available in the cluster:

```shell
kubectl api-resources | grep gateway.networking.k8s.io
```

## Usage Example

This example shows how to configure Envoy Gateway to accept HTTP traffic on a single external IP and route requests to two different services:

-   `http://app.example.com/service1 routes to service1`
-   `http://app.example.com/service2 routes to service2`

Start by creating a dedicated namespace:

```shell
kubectl create namespace envoy-example
```

### Create a ConfigMap

Create a file called `configmap.yaml` with HTML pages for both services:

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-pages
  namespace: envoy-example
data:
  service1.html: |
    <html>
      <head><title>Service 1</title></head>
      <body><h1>Service 1</h1></body>
    </html>
  service2.html: |
    <html>
      <head><title>Service 2</title></head>
      <body><h1>Service 2</h1></body>
    </html>
```

Apply the manifest:

```shell
kubectl apply -f configmap.yaml
```

### Create Deployments and Services

Create `service1.yaml`:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: service1
  namespace: envoy-example
spec:
  replicas: 2
  selector:
    matchLabels:
      app: service1
  template:
    metadata:
      labels:
        app: service1
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - name: html
              mountPath: /usr/share/nginx/html
      volumes:
        - name: html
          configMap:
            name: nginx-pages
            items:
              - key: service1.html
                path: index.html
---
apiVersion: v1
kind: Service
metadata:
  name: service1
  namespace: envoy-example
spec:
  selector:
    app: service1
  ports:
    - name: http
      port: 80
      targetPort: 80
  type: ClusterIP
```

Create `service2.yaml`:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: service2
  namespace: envoy-example
spec:
  replicas: 2
  selector:
    matchLabels:
      app: service2
  template:
    metadata:
      labels:
        app: service2
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - name: html
              mountPath: /usr/share/nginx/html
      volumes:
        - name: html
          configMap:
            name: nginx-pages
            items:
              - key: service2.html
                path: index.html
---
apiVersion: v1
kind: Service
metadata:
  name: service2
  namespace: envoy-example
spec:
  selector:
    app: service2
  ports:
    - name: http
      port: 80
      targetPort: 80
  type: ClusterIP
```

Apply both manifests:

```shell
kubectl apply -f service1.yaml
kubectl apply -f service2.yaml
```

Verify the pods are running:

```shell
kubectl get pods -n envoy-example
```

### Create a GatewayClass

`GatewayClass` connects the Gateway API to the Envoy Gateway controller.

Create `gatewayclass.yaml`:

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-gateway
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
```

Apply it:

```shell
kubectl apply -f gatewayclass.yaml
```

Check the status:

```shell
kubectl get gatewayclass envoy-gateway
```

The `ACCEPTED` column should show `True`, which means Envoy Gateway has registered the `GatewayClass` and will manage resources associated with it.

### Create a Gateway

The `Gateway` resource defines the external entry point for your application.

Create `gateway.yaml`:

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: app-gateway
  namespace: envoy-example
spec:
  gatewayClassName: envoy-gateway
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: Same
```

Apply it:

```shell
kubectl apply -f gateway.yaml
```

Once the `Gateway` is created, Envoy Gateway will provision an Envoy Proxy and a load balancer. You can track the load balancer creation in the [Hostman dashboard](https://hostman.com/my/balancer), or run:

```shell
kubectl get gateway app-gateway -n envoy-example
```

Wait until `PROGRAMMED` shows `True` and `ADDRESS` shows an external IP.

You can also inspect the service that was created:

```shell
kubectl get svc -n envoy-gateway-system \
  -l gateway.envoyproxy.io/owning-gateway-namespace=envoy-example,gateway.envoyproxy.io/owning-gateway-name=app-gateway
```

> [!NOTE]
> **Note**: Envoy Gateway automatically creates a `LoadBalancer` service for each `Gateway`, so you don't need to create a separate `Service` manifest as you would with a classic Ingress controller setup.

### Create an HTTPRoute

`HTTPRoute` defines the routing rules.

Create `httproute.yaml`:

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
  namespace: envoy-example
spec:
  parentRefs:
    - name: app-gateway
  hostnames:
    - app.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /service1
      filters:
        - type: URLRewrite
          urlRewrite:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /
      backendRefs:
        - name: service1
          port: 80
    - matches:
        - path:
            type: PathPrefix
            value: /service2
      filters:
        - type: URLRewrite
          urlRewrite:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /
      backendRefs:
        - name: service2
          port: 80
```

Apply it:

```shell
kubectl apply -f httproute.yaml
```

Confirm the route is accepted:

```shell
kubectl get httproute app-route -n envoy-example -o yaml
```

The resource status should include:

```shell
status: "True" type: Accepted
```

This `HTTPRoute` accepts requests for `app.example.com` and routes them to different services based on the path prefix. The `URLRewrite` filter strips the `/service1` and `/service2` prefixes, replacing them with `/`, so Nginx inside each service serves its main page correctly.

### Verify the Setup

Get the external IP of the Gateway:

```shell
kubectl get gateway app-gateway -n envoy-example
```

Add this IP as an A record for app.example.com in your DNS settings. Once DNS propagates, test both endpoints:

```shell
curl http://app.example.com/service1
curl http://app.example.com/service2
```

If DNS hasn't propagated yet, you can test by IP using the `Host` header:

```shell
curl -H "Host: app.example.com" http://<EXTERNAL_IP>/service1
curl -H "Host: app.example.com" http://<EXTERNAL_IP>/service2
```

Both requests should return the respective Service 1 and Service 2 pages.

### Clean Up

To remove all resources created in this example, run:

```shell
kubectl delete namespace envoy-example
kubectl delete gatewayclass envoy-gateway
```

This does not uninstall the Envoy Gateway add-on. To remove it, go to the **Addons** tab in the cluster dashboard.

## Migrating from Nginx Ingress to Envoy Gateway

You can run Envoy Gateway alongside Nginx Ingress and migrate routes gradually. This lets you test Gateway API resources on a separate external IP before switching DNS records or redirecting traffic to the new Gateway.

To automate the conversion of existing Ingress resources, use the [ingress2gateway](https://github.com/kubernetes-sigs/ingress2gateway) utility. It reads Ingress resources from your cluster and outputs equivalent Gateway API resources: `Gateway`, `HTTPRoute`, and related objects.

> [!NOTE]
> **Important**: `ingress2gateway` is a helpful starting point, but it is not a fully automatic migration tool. Some Nginx Ingress annotations may not be supported or may require manual configuration after conversion.

### Prerequisites

Before migrating, make sure your cluster is ready:

-   Envoy Gateway is installed.
-   A `GatewayClass` for Envoy Gateway has been created.
-   Existing Ingress resources are working correctly through Nginx Ingress.

Check that both classes are present:

```shell
kubectl get ingressclass
kubectl get gatewayclass
```

The output should include an `IngressClass` named `nginx` and a `GatewayClass` named `envoy-gateway`.

### Install ingress2gateway

If you have Go installed locally, run:

```shell
go install github.com/kubernetes-sigs/ingress2gateway@v1.0.0
```

The binary will be placed in `$(go env GOPATH)/bin`. Make sure this directory is in your `PATH`.

Alternatively, install via Homebrew:

```shell
brew install ingress2gateway
```

Or download a prebuilt binary from the [project releases page](https://github.com/kubernetes-sigs/ingress2gateway/releases).

### Source Setup

In this example, the migration targets an application already published through Nginx Ingress.

The cluster has a namespace called `ingress-migration-demo` containing:

-   An Ingress resource named `migration-source`
-   Services `service1` and `service2`
-   Two routing rules for the domain `ingress-migration.example.com`:
    -   `/service1` routes to `service1`
    -   `/service2` routes to `service2`

Verify the source Ingress:

```shell
kubectl get ingress -n ingress-migration-demo
```

The `CLASS` column should show `nginx`.

### Convert Ingress Resources

To convert the Ingress to Gateway API resources, run:

```shell
ingress2gateway print \
 --providers=ingress-nginx \
 --emitter=envoy-gateway \
 --ingress-nginx-ingress-class=nginx \
 --namespace=ingress-migration-demo > gateway-migration.yaml
```

The flags in this command:

-   `print` outputs the generated resources to stdout; the `>` redirect saves them to `gateway-migration.yaml`.
-   `--providers=ingress-nginx` specifies the source Ingress controller type.
-   `--emitter=envoy-gateway` sets Envoy Gateway as the target controller.
-   `--ingress-nginx-ingress-class=nginx` filters for Ingress resources using the `nginx` class.
-   `--namespace=ingress-migration-demo` scopes the conversion to the source namespace.

### Review and Fix the Output

Open `gateway-migration.yaml` and review the generated resources before applying them.

Pay attention to the `gatewayClassName` field in the `Gateway` resource. The tool may generate this value based on the source `IngressClass` name, resulting in something like:

```yaml
spec:
  gatewayClassName: nginx
```

Envoy Gateway will not process a `Gateway` with this class name unless a `GatewayClass` named `nginx` exists in the cluster. Update it to match the `GatewayClass` you created earlier:

```yaml
spec:
  gatewayClassName: envoy-gateway
```

### Apply the Gateway API Resources

Once you're satisfied with the output, apply it:

```shell
kubectl apply -f gateway-migration.yaml
```

Verify the `Gateway` has been created and has received an external IP:

```shell
kubectl get gateway -A
```

Check that the `HTTPRoute` has been accepted by the controller:

```shell
kubectl get httproute -A
kubectl describe httproute <route-name> -n <namespace>
```

The `HTTPRoute` status should show both `Accepted=True` and `ResolvedRefs=True`.

### Test Before Switching Traffic

While your DNS records still point to the old Nginx Ingress, you can verify the new `Gateway` by sending requests directly to its external IP with a `Host` header:

```shell
curl -H "Host: app.example.com" http://<EXTERNAL_IP>/<path>
```

Where:

-   `app.example.com` is the domain from your `HTTPRoute`.
-   `<EXTERNAL_IP>` is the IP address of the new `Gateway`.
-   `<path>` is the path handled by the route.

Compare the responses from the old Ingress and the new `Gateway`. If everything looks correct, update your DNS records to point to the new load balancer's IP.
