---
title: "NGINX Ingress for Kubernetes | Hostman Docs"
description: "Learn how to set up and configure Kubernetes Ingress using NGINX Ingress Controller. Step-by-step guide to deploying multiple services, configuring routing rules, exposing applications via LoadBalancer, and securing traffic with HTTPS using TLS certificates or cert-manager."
---

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

Ingress is a Kubernetes resource that enables access to services within a cluster via HTTP and HTTPS. It provides routing based on URLs, domains, and other parameters. This article explores the use of Nginx Ingress with three Nginx deployments simulating three different services. We will also configure an SSL certificate to secure the connection.

## Installing Nginx Ingress

First, Nginx Ingress needs to be installed in the cluster.

1.  Go to the **Kuberenetes** section and click on the cluster.
2.  In the **Addons** tab, click on the three dots next to **Nginx Ingress**.
3.  Select **Install**.

After installation, verify that Ingress is functioning correctly by running the command:

```shell
kubectl get pods -n ingress-nginx
```

Ensure that all pods have the `Running` status.

## Configuring Deployments and Services

Let’s consider an example of using NGINX Ingress. We will deploy three `Deployments` based on Nginx images, each simulating a separate service. For each `Deployment`, we will create a `Service` of type `ClusterIP`—the default service type in Kubernetes—which provides an internal IP address within the cluster. This type of service is not directly accessible from outside the cluster; however, it can be exposed to external traffic using an Ingress.

For convenience, we will create a separate namespace that will contain all the manifests:

```shell
kubectl create namespace ingress-example
```

To distinguish the services from each other, we will create a `ConfigMap` containing three different HTML pages. Create a file named `configmap.yaml` with the following content:

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: service-config
  namespace: ingress-example
data:
  service1.html: |
    <html>
      <head><title>Service 1</title></head>
      <body><h1>Welcome to Service 1!</h1></body>
    </html>
  service2.html: |
    <html>
      <head><title>Service 2</title></head>
      <body><h1>Welcome to Service 2!</h1></body>
    </html>
  service3.html: |
    <html>
      <head><title>Service 3</title></head>
      <body><h1>Welcome to Service 3!</h1></body>
    </html>
```

Next, we will define the `Deployments`.

**service1-deployment.yaml**:

```yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: service1
  namespace: ingress-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: config-volume
          mountPath: /usr/share/nginx/html
      volumes:
      - name: config-volume
        configMap:
          name: service-config
          items:
          - key: service1.html
            path: index.html
---
apiVersion: v1
kind: Service
metadata:
  name: service1
  namespace: ingress-example
spec:
  selector:
    app: service1
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP
```

Manifest explanation:

-   `Deployment`
    -   `replicas`: `2` specifies that two replicas of Nginx pods will be running.
    -   The `selector` and `template.metadata.labels` section defines how the service will locate the pods.
    -   `volumeMounts` and `volumes` are used to mount the `service1.html` file from a `ConfigMap` into the directory from which Nginx serves content by default.
-   `Service`
    -   `ClusterIP` means the service will be accessible only within the cluster via an assigned IP address. For external access, we will use Ingress.

The manifests for `service2` and `service3` are similar, differing only in the service name and the HTML file used.

**service2-deployment.yaml**:

```yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: service2
  namespace: ingress-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: config-volume
          mountPath: /usr/share/nginx/html
      volumes:
      - name: config-volume
        configMap:
          name: service-config
          items:
          - key: service2.html
            path: index.html
---
apiVersion: v1
kind: Service
metadata:
  name: service2
  namespace: ingress-example
spec:
  selector:
    app: service2
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP
```

**service3-deployment.yaml**:

```yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: service3
  namespace: ingress-example
spec:
  replicas: 2
  selector:
    matchLabels:
      app: service3
  template:
    metadata:
      labels:
        app: service3
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: config-volume
          mountPath: /usr/share/nginx/html
      volumes:
      - name: config-volume
        configMap:
          name: service-config
          items:
          - key: service3.html
            path: index.html
---
apiVersion: v1
kind: Service
metadata:
  name: service3
  namespace: ingress-example
spec:
  selector:
    app: service3
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP
```

Apply the maniests:

```shell
kubectl apply -f configmap.yaml
kubectl apply -f service1-deployment.yaml
kubectl apply -f service2-deployment.yaml
kubectl apply -f service3-deployment.yaml
```

Check the pods statuses:

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

All pods should have the `Running` status. 

## Creating Ingress

With Ingress, we will define which traffic (based on domains and paths) should be redirected to the corresponding services. We will create the `ingress.yaml` manifest.

```yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  namespace: ingress-example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: ingress1.example.com
    http:
      paths:
      - path: /service1
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80
      - path: /service2
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80
  - host: ingress2.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service3
            port:
              number: 80
```

Manifest explanation:

-   `rules.host` specifies the domain to which Ingress will be bound.
-   `paths` defines the routing rules:  
    -   Requests to `ingress1.example.com/service1` will be redirected to `service1`.
    -   Requests to `ingress1.example.com/service2` will be redirected to `service2`.
    -   Requests to `ingress2.example.com/` will be redirected to `service3`.

Apply the manifest:

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

## Creating a LoadBalancer

To access Nginx Ingress from an external network, we will create a `Service` of type `LoadBalancer`, which will handle load balancing and provide an external IP address. We will prepare the `loadbalancer.yaml` manifest for this.

```yml
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
spec:
  selector:
    app.kubernetes.io/name: ingress-nginx
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: https
      port: 443
      targetPort: 443
  type: LoadBalancer
```

Apply the manifest:

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

Wait until the load balancer is created and verify that it has an external IP address:

```shell
kubectl get services -n ingress-nginx
```

The external IP will be displayed in the `EXTERNAL-IP` column.

Add the external IP to the domain’s DNS settings as A records for the domains `ingress1.example.com` and `ingress2.example.com`. Once the DNS updates, your domains will point to this load balancer, and you will be able to access the services via external URLs:

-   `http://ingress1.example.com/service1` — will display the header "Welcome to Service 1!".
-   `http://ingress1.example.com/service2` — will display the header "Welcome to Service 2!".
-   `http://ingress2.example.com/` — will display the header "Welcome to Service 3!".

## Configuring an SSL Certificate

To ensure a secure connection in Nginx Ingress, you need to add an existing SSL certificate as a Kubernetes Secret and specify this secret in the Ingress manifest.

If you want to automate certificate issuance and renewal (e.g., using Let’s Encrypt), use cert-manager.

### Preparing Certificate and Key Files

Save the `tls.key` (private key) and `tls.crt` (certificate) files locally. Ensure that the certificate is not expired and matches the domains you want to secure.

### Creating a Kubernetes Secret

To add the certificate to the cluster, encode the files in Base64 and create a `Secret` manifest. Run the following commands in the terminal:

```shell
base64 -w 0 ./tls.crt
base64 -w 0 ./tls.key
```

Then, create the `tls-secret.yaml` file with the following content:

```yml
apiVersion: v1
kind: Secret
metadata:
  name: ingress-example-tls
  namespace: ingress-example
type: kubernetes.io/tls
data:
  tls.crt: |-
    <BASE64_ENCODED_CERTIFICATE>
  tls.key: |-
    <BASE64_ENCODED_KEY>
```

Save the file and apply the manifest:

```shell
kubectl apply -f tls-secret.yaml
```

Verify that the key and certificate values are not empty by running:

```shell
kubectl describe secret ingress-example-tls -n ingress-example
```

### Updating the Ingress Manifest

Update your `ingress.yaml` manifest by adding the `tls` section and specifying the created secret.

```yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  namespace: ingress-example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  tls:
  - hosts:
    - ingress1.example.com
    - ingress2.example.com
    secretName: ingress-example-tls
  rules:
  - host: ingress1.example.com
    http:
      paths:
      - path: /service1
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80
      - path: /service2
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80
  - host: ingress2.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service3
            port:
              number: 80
```

Apply the updated manifest:

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

Now, when accessing `ingress1.example.com` or `ingress2.example.com` over HTTPS, the browser will use the certificate stored in the `ingress-example-tls` secret. This ensures that traffic to your services is secured.

If you have multiple certificates for different domains, create a `Secret` for each of them. For example, for `example.com` and `example.org`, create two separate secrets.

Then, update the Ingress manifest by adding a `tls` section for each domain.

```yml
spec:
  tls:
  - hosts:
    - example.com
    secretName: example-com-tls
  - hosts:
    - example.org
    secretName: example-org-tls
```

Each domain will then use its respective certificate as specified in the corresponding secret.
