Set Up Nginx Ingress Controller with SSL on Kubernetes

The Nginx Ingress Controller is one of the most widely used Ingress solutions for Kubernetes. It leverages Nginx as both a reverse proxy and load balancer to securely channel external requests to services inside your cluster. Acting as a central gateway, it provides SSL/TLS termination, load balancing, session persistence, and path-based routing for various applications running in Kubernetes.

In this tutorial, you will configure an Nginx Ingress Controller with SSL certificates inside a Kubernetes Engine cluster. You’ll deploy two sample applications and secure them using Let’s Encrypt certificates managed by Cert Manager. The guide also demonstrates how to integrate commercial SSL certificates for TLS encryption.

Prerequisites

Before proceeding, ensure you have the following ready:

  • A running Kubernetes cluster with at least two nodes.
  • Kubectl installed for managing your cluster.
  • The Helm package manager set up on your local system.
  • A valid domain name, for instance example.com.

Install the Nginx Ingress Controller

First, add the Nginx Ingress Helm repository.

$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

Update Helm repositories to fetch the latest charts.

Install the Nginx Ingress Controller into your Kubernetes environment.

$ helm install ingress-nginx ingress-nginx/ingress-nginx

After installation, a LoadBalancer service is automatically provisioned for the controller.

$ kubectl get services ingress-nginx-controller

Output example:

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.101.22.249 <pending> 80:31915/TCP,443:30217/TCP 106s

It may take several minutes for the service to obtain an EXTERNAL-IP, depending on your cloud provider.

Install Cert Manager

Deploy the latest release of Cert Manager using the command below.

$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml

You can always check the official Cert Manager release page for newer versions.

Verify Cert Manager resources within the cert-manager namespace.

$ kubectl get all -n cert-manager

You should see several pods, services, replicasets, and deployments related to Cert Manager.

Deploy Backend Applications

Next, you’ll deploy two simple applications—app1 and app2—for testing the Ingress configuration. Both will use the hashicorp/http-echo image that serves a text response on an HTML page.

Create the Deployment for App1

Add the following configuration:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app1
  template:
    metadata:
      labels:
        app: app1
    spec:
      containers:
      - name: app1
        image: hashicorp/http-echo
        args: ["-text=Hello from App1"]
        ports:
        - containerPort: 5678

Create the Deployment for App2

Insert this configuration:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app2
  template:
    metadata:
      labels:
        app: app2
    spec:
      containers:
      - name: app2
        image: hashicorp/http-echo
        args: ["-text=Hello from App2"]
        ports:
        - containerPort: 5678

Apply both deployment manifests:

$ kubectl apply -f app1-deploy.yaml
$ kubectl apply -f app2-deploy.yaml

Confirm that the deployments are active:

Create Service Manifests

Service for App1

Insert the following YAML:

apiVersion: v1
kind: Service
metadata:
  name: app1-svc
spec:
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app: app1

Service for App2

Add this configuration:

apiVersion: v1
kind: Service
metadata:
  name: app2-svc
spec:
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app: app2

Deploy both services:

$ kubectl apply -f app1-svc.yaml
$ kubectl apply -f app2-svc.yaml

Check that all services are running:

Configure DNS Records

Log in to your DNS provider and open your domain management console. Create an A record for app1 pointing to your LoadBalancer’s external IP address. Repeat the process for app2, using the same IP.

Expose Applications via Nginx Ingress Controller

Now create Ingress resources to expose your backend applications publicly.

Ingress for App1

Insert this content:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-app1
  annotations:
    cert-manager.io/issuer: letsencrypt-nginx
spec:
  ingressClassName: nginx
  rules:
  - host: app1.example.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: app1-svc
            port:
              number: 80

Ingress for App2

Use this YAML content:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-app2
  annotations:
    cert-manager.io/issuer: letsencrypt-nginx
spec:
  ingressClassName: nginx
  rules:
  - host: app2.example.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: app2-svc
            port:
              number: 80

Apply both Ingress manifests:

$ kubectl apply -f app1-ingress.yaml
$ kubectl apply -f app2-ingress.yaml

Confirm the Ingress resources:

You should see both ingress-app1 and ingress-app2 entries. Wait until the ADDRESS column shows your LoadBalancer IP.

Enable SSL Certificates for Nginx Ingress

To enable HTTPS, you can configure Cert Manager to automatically manage SSL certificates for your cluster. Cert Manager extends Kubernetes with custom resources for certificate issuance and lifecycle management from trusted Certificate Authorities like Let’s Encrypt.

  • Issuer: Defines configuration for a certificate issuer, such as the type (ACME) and challenge method (DNS01 or HTTP01) within a namespace.
  • ClusterIssuer: Similar to Issuer but can issue certificates cluster-wide, suitable for multi-namespace configurations.
  • Certificate: Specifies certificate properties including domain names, secret storage, and the chosen issuer.

List available CRDs for Cert Manager using the following command:

$ kubectl get crd -l app.kubernetes.io/name=cert-manager

The output should include resources like issuers.cert-manager.io, certificates.cert-manager.io, and clusterissuers.cert-manager.io, confirming Cert Manager’s CRDs are available.

Set Up Let’s Encrypt Certificates

Create a new Issuer manifest named cert-issuer.yaml to configure Let’s Encrypt certificate management.

Add the following YAML configuration:

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-nginx
spec:
  acme:
    email: hello@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-nginx-prod
    solvers:
    - http01:
        ingress:
          class: nginx

This configuration defines an ACME Issuer with the following parameters:

  • email: The contact email associated with the ACME account. Avoid using @example.com as it will prevent the issuer from becoming active.
  • server: The API endpoint for the Let’s Encrypt ACME service.
  • privateKeySecretRef: Specifies the Kubernetes Secret where the ACME account’s private key is stored.
  • solvers: Defines how Let’s Encrypt validates domain ownership. Typically, the http01 method is used unless wildcard certificates are required.

Replace the placeholder email address with your valid one, then save and close the file.

Apply the Issuer resource to your cluster:

$ kubectl apply -f cert-issuer.yaml

Check if the Issuer resource is properly created and initialized:

Sample output:

NAME READY AGE
letsencrypt-nginx False 4s

The READY column will initially show False. After a short time, it should change to True.

Configure Ingress Resources for TLS

Next, link your Ingress resources with the Let’s Encrypt certificates.

Check the current state of Ingress resources:

Edit the app1-ingress.yaml file to include a TLS section:

Add this snippet under the spec section:

tls:
  - hosts:
    - app1.example.com
    secretName: letsencrypt-nginx-app1

The full manifest now appears as follows:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-app1
  annotations:
    cert-manager.io/issuer: letsencrypt-nginx
spec:
  ingressClassName: nginx
  rules:
  - host: app1.example.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: app1-svc
            port:
              number: 80
  tls:
  - hosts:
    - app1.example.com
    secretName: letsencrypt-nginx-app1

Apply the same TLS section to the app2-ingress.yaml manifest:

tls:
  - hosts:
    - app2.example.com
    secretName: letsencrypt-nginx-app2

Apply the updated manifests to enable TLS on both hosts:

$ kubectl apply -f app1-ingress.yaml
$ kubectl apply -f app2-ingress.yaml

Verify that your Ingress resources have port 443 activated:

Example output:

NAME CLASS HOSTS ADDRESS PORTS AGE
app1-ingress nginx app1.example.com 192.0.2.1 80,443 10m
app2-ingress nginx app2.example.com 192.0.2.1 80,443 10m

Check Certificate Resources

Confirm that the SSL certificates were generated successfully:

$ kubectl get certificates

Example output:

NAME READY SECRET AGE
letsencrypt-app1 True letsencrypt-nginx-app1 5m
letsencrypt-app2 True letsencrypt-nginx-app2 5m

If the READY column reads True, your certificates are correctly issued. If False, double-check your Ingress configuration and reapply the manifests to renew certificates.

Test the SSL Configuration

Once certificates are active, verify HTTPS access through a browser:

If accessed via HTTP, the Ingress Controller automatically redirects traffic to HTTPS.

Import Commercial SSL Certificates

If you purchased an SSL certificate from a commercial Certificate Authority, you can manually import it. First, convert both the certificate and private key files into base64 format.

$ base64 -w 0 /path/ssl-certificate.pem
$ base64 -w 0 /path/cert-private-key.pem

Replace /path with the actual directory containing your SSL files.

Next, create a Kubernetes Secret manifest:

Insert the following YAML configuration:

apiVersion: v1
kind: Secret
metadata:
  name: prod-ssl-secret
type: kubernetes.io/tls
data:
  tls.crt: 
  tls.key: 

Ensure that:

  • tls.crt holds the base64-encoded SSL certificate.
  • tls.key contains the base64-encoded private key.

Save the file and apply it. Then, reference this Secret under spec.tls.secretName in your Ingress manifest to activate the commercial SSL certificate.

Troubleshooting

  • Nginx 502 Gateway Error: Verify the Ingress rules and ensure all backend services are reachable.
  • Let’s Encrypt Certificates not generating (READY = False): Confirm your Issuer and Certificate configurations, use a valid email address, and review the Issuer logs for errors.

Conclusion

In this guide, you successfully installed and configured the Nginx Ingress Controller with SSL on a Kubernetes Engine cluster. Your setup now routes external traffic securely over HTTPS, using either Let’s Encrypt or commercial SSL certificates for encrypted access to your Kubernetes services.

Source: vultr.com

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in: