App Ingress

An App Ingress is an L7 ingress that allows application developers to directly harness the power of Envoy that is available. Unlike the other types of Ingress that are available in TSB, configuring it does not require admin privileges, and exposes the features of Envoy proxy in such a way that it’s easier for application developers to define intent.

The App Ingress is a stripped down version of Istio using Istiod as the control plane component and Istio IngressGateway (i.e. Envoy proxy) as the data plane component. The App Ingress is deployed per application in the namespace owned by the application, and can only consume Istio configurations from the namespace where it is deployed in.

The App Ingress also has an OpenAPI translator add-on that allows the user to configure the ingress using an OpenAPI specification.

This feature requires tctl version 1.4.5 or newer.

Configuration Using Istio

In this example you will install httpbin in a namespace, and create an App Ingress in the same namespace to route access to the httpbin workload through.

Create a namespace named httpbin-appingress.

kubectl create namespace httpbin-appingress

You will be installing the workload and the App Ingress in the same namespace.

In this example the workload will be the httpbin service. Install httpbin by following these instructions.

Install the App Ingress using the following command.

tctl experimental app-ingress kubernetes generate -n httpbin-appingress | \
  kubectl apply -f -

:::note You might see the error unable to recognize "STDIN": no matches for kind "IstioOperator" in version "". If you encounter this error, please re-run the above command. This happens when the IstioOperator CRD has not been deployed yet, and usually it will go away if you retry :::

Verify that the httpbin, istio-ingressgateway, and istiod pods are running properly in namespace httpbin-appingress:

kubectl get pod -n httpbin-appingress

NAME                                    READY   STATUS    RESTARTS   AGE
httpbin-74fb669cc6-lc4qm                1/1     Running   0          10m
istio-ingressgateway-6f9c469bd5-r7z4t   1/1     Running   0          8m8s
istio-operator-1-11-3-f88d885b5-8wb9k   1/1     Running   0          8m42s
istiod-1-11-3-597999c56f-5f2xr          1/1     Running   0          8m19s

You will need to access the httpbin service via the host name To do this, deploy an Istio Gateway and Virtual Service in the httpbin-appingress namespace to route HTTP traffic that intended for to the httpbin pod.

Create a file named httpbin-appingress-virtualservice.yaml with the following contents:

kind: Gateway
  name: httpbin-gateway
    istio: ingressgateway # use Istio default gateway implementation
  - port:
      number: 80
      name: http
      protocol: HTTP
    - ""
kind: VirtualService
  name: httpbin
  - ""
  - httpbin-gateway
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    - destination:
          number: 8000
        host: httpbin

Apply this using kubectl:

kubectl -n httpbin-appingress -f httpbin-appingress-virtualservice.yaml

Since you have not setup DNS for, you will need to setup your environment or change the way you issue HTTP requests to access the service that you have created. In this example you will use kubectl port-forward to establish port forwarding.

In a different terminal, set up port-forwarding to the istio-ingressgateway service in the httpbin-appingress namespace using local port 4040:

kubectl -n httpbin-appingress port-forward svc/istio-ingressgateway 4040:80 

You should now be able to reach the httpbin application in the httpbin-appingress namespace through the istio-ingressgateway service using the following command:

curl -s -I \
  -H "Host:" \

Using the OpenAPI Translator

If your application provides an OpenAPI specification (3.0.0 or higher), you can use it to generate the routing rules to your application. The OpenAPI Translator add-on takes the application’s OpenAPI specification and translates it to Istio configuration and applies it to the App Ingress.

In this example you will use the bookinfo sample application and use its OpenAPI specification.

Create a new namespace bookinfo-openapi:

kubectl create namespace bookinfo-openapi

Deploy the bookinfo sample into the bookinfo-openapi namespace:

kubectl apply -n bookinfo-openapi \

Once you verify that the application has been properly deployed, deploy the App Ingress in the bookinfo-openapi namespace. You will need to specify the “backend” service (application) that the OpenAPI specification is describing as well.

tctl experimental app-ingress kubernetes generate \
  -n bookinfo-openapi \
  --openapi-translator \
  --openapi-backend-service http://productpage.bookinfo-openapi.svc.cluster.local:9080 \
  | kubectl apply -f - 

The previous command creates an App Ingress that expects an OpenAPI specification in the ConfigMap named openapi-translator. Since you have not yet provided a specification the App Ingress cannot be configured properly.

You will have to obtain an OpenAPI spec for bookinfo, but the sample that is provided with Istio only comes in OpenAPI 2.0 format. A version of the bookinfo OpenAPI specification that has been converted to OpenAPI 3.0.0 is available through this link. Download the file as bookinfo-openapi.yaml.

Create the ConfigMap using this file using the following command:

kubectl -n bookinfo-openapi create configmap openapi-translator \

When the configuration is picked up by the OpenAPI Translator, Istio resources such as Gateway and VirtualService will be available in the namespace. You verify this by issuing kubectl get gateway and kubectl get virtualservice commands:

kubectl -n bookinfo-openapi get gateway

NAME                                    AGE
istio-ingressgateway-f6fb54b17b9120eb   64s
kubectl -n bookinfo-openapi get virtualservice

NAME                                                     GATEWAYS                                                     HOSTS                  AGE
istio-ingressgateway-f6fb54b17b9120eb-www-bookinfo-com   ["bookinfo-openapi/istio-ingressgateway-f6fb54b17b9120eb"]   [""]   5m13s

It is also possible to leverage more TSB features such as rate limiting, authentication, and authorization by adding annotations to your OpenAPI specification.

Extending App Ingress using the IstioOperator

It is possible to further configure the Istio components within App Ingress using the Istio Operator.

For example, if you want to plug in custom CA certificates (such a config is particularly useful in Kubernetes versions higher than 1.22 where the kubernetes pilotCertProvider is deprecated), create a file named configure-plug-in-certs.yaml:

kind: IstioOperator
  namespace: bookinfo-appingress
  name: bookinfo-appingress 
      pilotCertProvider: istiod

Create the cacerts secret that contain the certificate and keys. For more information click here. You can test this out by using the sample certs provided in the Istio release bundle. Run this command to download the Istio release bundle.

curl -L | ISTIO_VERSION=1.11.3 sh -

Run this commend to create the cacerts secret.

kubectl create secret generic cacerts -n bookinfo-appingress --from-file=ca-cert.pem=istio-1.11.3/samples/certs/ca-cert.pem --from-file=ca-key.pem=istio-1.11.3/samples/certs/ca-key.pem --from-file=root-cert.pem=istio-1.11.3/samples/certs/root-cert.pem --from-file=cert-chain.pem=istio-1.11.3/samples/certs/cert-chain.pem

Then supply this file when you generate the manifest for App Ingress by specifying the -f (--filename) flag:

tctl experimental app-ingress kubernetes generate \
  -n bookinfo-appingress \
  -f configure-plug-in-certs.yaml \
  | kubectl apply -f -

:::note If running multiple App Ingresses in a single cluster and you are using custom cacerts, make sure to use the same cacerts secret in each namespace where App Ingress is running. :::

App Ingress in Docker

If you have restrictions deploying to your main Kubernetes environment, it is possible to deploy App Ingress to Docker via docker-compose. This might be a requirement if you want to run your App Ingress in an environment other than Kubernetes, or you might want to test your application and/or OpenAPI specification locally.

In this example you will start a service in docker, where traffic will be received via App Ingress created using docker-compose, and configured using the OpenAPI document of the service

Make sure to have docker and docker-compose installed before proceeding.

Generating the docker-compose file

Create a directory named appingress-compose. Later instructions will rely on this directory being present.

Generate and save the generated docker-compose file in the appingress-compose directory that defines all the App Ingress containers using the following command. Notice that --openapi-translator option is enabled, and that the backend service is specified through --openapi-backend-service.

tctl x app-ingress docker-compose generate \
  --openapi-translator \
  --output-dir appingress-compose \
  --openapi-backend-service \

Running docker-compose

Lets run the containers using docker-compose

$ cd appingress-compose
$ docker-compose up -d

You should see the App Ingress containers starting, as well as a new docker network named appingress-compose_app-ingress being created.

$ docker ps --filter="name=appingress"
CONTAINER ID   IMAGE                                                                                          COMMAND                  CREATED       STATUS       PORTS                                            NAMES
aeae400dcdc3   istio/proxyv2:1.11.3                                                                           "/usr/local/bin/pilo…"   2 hours ago   Up 2 hours>8080/tcp,>8443/tcp   appingress-compose_istio-ingressgateway_1
e7d988a02384   "/usr/local/bin/geni…"   2 hours ago   Up 2 hours                                                    appingress-compose_openapi-translator_1
19539c0a28d3   istio/pilot:1.11.3                                                                             "/usr/local/bin/pilo…"   2 hours ago   Up 2 hours                                                    appingress-compose_pilot-discovery_1
$ docker network ls
NETWORK ID     NAME                             DRIVER    SCOPE
d5b159e5b631   appingress-compose_app-ingress   bridge    local
51364ba39b1b   bridge                           bridge    local
7135f2f769e4   host                             host      local
c955a05b02d1   none                             null      local

Run an Application Container

Start a container with the same name mentioned in the --openapi-backend-service argument, which should be in this case. The current implementation requires that the name matches the backend service name. You also need to deploy it in the same appingress-compose_app-ingress network which was recently created by docker-compose

docker run --net appingress-compose_app-ingress --name -d kennethreitz/httpbin

Install the OpenAPI specification

Download the file httpbin-openapi.json and save it under the .app-ingress/config-sources directory as .app-ingress/config-sources/httpbin-openapi.json. The App Ingress will consume and translate it into Istio resources.

You should instantaneously see the generated Istio resource being created as a YAML file:

$ ls .app-ingress/config-sources/app-ingress.yaml

You can include additional Istio resources such as Destination Rules, Envoy filters in this directory .app-ingress/config-sources/ to implement your use case.


If everything is working, you should be able to access the application running in Docker.

$ curl -vvv -H "Host:" http://localhost:8080/get
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /get HTTP/1.1
> Host:
> User-Agent: curl/7.64.1
> Accept: */*
< HTTP/1.1 200 OK
< server: istio-envoy
< date: Wed, 08 Dec 2021 03:50:43 GMT
< content-type: application/json
< content-length: 432
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 1
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "", 
    "User-Agent": "curl/7.64.1", 
    "X-B3-Sampled": "0", 
    "X-B3-Spanid": "e5ab7bfbd817196b", 
    "X-B3-Traceid": "2729efd79fd5e2e9e5ab7bfbd817196b", 
    "X-Envoy-Attempt-Count": "1", 
    "X-Envoy-Decorator-Operation": "", 
    "X-Envoy-Internal": "true"
  "origin": "", 
  "url": ""
* Connection #0 to host localhost left intact
* Closing connection 0