How to Set up Nginx Ingress Controller with SSL on Kubernetes

Updated on 06 May, 2025
How to Set up Nginx Ingress Controller with SSL on Kubernetes header image

Nginx Ingress Controller is a popular Kubernetes Ingress controller that uses Nginx as a reverse proxy and load balancer to securely route external traffic to services in a cluster. It works as the single access point for underlying services while offering SSL/TLS termination, load balancing, session handling, and path-based routing for services in the Kubernetes cluster.

In this article, you will set up a Nginx Ingress Controller with SSL in a Kubernetes Engine cluster. You will deploy two applications and issue Let’s Encrypt certificates with Cert Manager to secure them. You will also see how you can import commercial SSL certificates for TLS encryption.

Prerequisites

Before you begin, make sure you:

Install the Nginx Ingress Controller

  1. Add the Nginx Ingress repository.

    console
    $ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
    
  2. Update Helm.

    console
    $ helm repo update
    
  3. Install the Nginx Ingress Controller.

    console
    $ helm install ingress-nginx ingress-nginx/ingress-nginx
    
  4. After installation, a Load Balancer is automatically added to your cluster.

    console
    $ kubectl get services ingress-nginx-controller
    

    Output:

    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 some time for the service to get an EXTERNAL-IP, depending on your cloud provider.

Install CertManager

  1. Install the latest CertManager version.

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

    Visit the official CertManager releases page to get the latest version.

  2. Inspect the CertManager Kubernetes resources.

    console
    $ kubectl get all -n cert-manager
    

    You should see multiple resources of kind pod, service, replicaset, and deployment, all related to your CertManager.

Deploy Backend Applications

In this section, you'll deploy example applications to test your Nginx Ingress controller. For purposes of this article, deploy two example applications, app1 and app2 using the http-echo image that outputs the command line argument on a html page.

  1. Create a manifest file app1-deploy.yaml for Deployment app1.

    console
    $ nano app1-deploy.yaml
    
  2. Add the following content to the file.

    yaml
    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
    

    Save and close the file.

  3. Create the second manifest file app2-deploy.yaml for Deployment app2.

    console
    $ sudo nano app2-deploy.yaml
    
  4. Add the following content to the file.

    yaml
    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
    

    Save and close the file.

  5. Apply the manifest for the app1 deployment.

    console
    $ kubectl apply -f app1-deploy.yaml
    
  6. Apply the manifest for the app2 deployment.

    console
    $ kubectl apply -f app2-deploy.yaml
    
  7. Verify that your deployments are successful.

    console
    $ kubectl get deployments
    

    Your should see the app1 and app2 deployments listed.

  8. Create a new app1-svc.yaml service manifest.

    console
    $ nano app1-svc.yaml
    
  9. Add the following content to the file.

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

    Save and close the file.

  10. Create another service manifest file app2-svc.yaml.

    console
    $ nano app2-svc.yaml
    
  11. Add the following content to the file.

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

    Save and close the file.

  12. Deploy the app1-svc service.

    console
    $ kubectl apply -f app1-svc.yaml
    
  13. Deploy the app2-svc service.

    console
    $ kubectl apply -f app2-svc.yaml
    
  14. Verify that all services are running.

    console
    $ kubectl get services
    

    Your should see the app1-svc and app2-svc services listed.

Setup DNS Records

  1. Login to your DNS Provider account. For example, Vultr DNS.

  2. Access your domain.

  3. Set up a new domain A subdomain record with the value app1 that points to your LoadBalancer’s external IP Address.

  4. Set up another A subdomain record with the value app2 that points to the same IP Address.

    Setup a Domain A Record on Vultr

Configure the Nginx Ingress Controller to Expose backend Applications

In this section, you'll create Ingress resources for both the applications in the cluster and test them.

  1. Create a new manifest file app1-ingress.yaml to create an Ingress resource for app1 Deployment.

    console
    $ sudo nano app1-ingress.yaml
    
  2. Add the following content to the file.

    yaml
    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
    

    Replace app1.example.com with your domain name. Save and close the file.

  3. Create another manifest file app2-ingress.yaml to create an Ingress resource for app2 Deployment.

    console
    $ sudo nano app2-ingress.yaml
    
  4. Add the following content to the file.

    yaml
    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
    

    Replace app2.example.com with your domain name. Save and close the file.

  5. Apply the manifest for the ingress-app1 Ingress resource.

    console
    $ kubectl apply -f app1-ingress.yaml
    
  6. Apply the manifest for the ingress-app2 Ingress resource.

    console
    $ kubectl apply -f app2-ingress.yaml
    
  7. Verify that the Ingress resources are available.

    console
    $ kubectl get ingress
    

    You should see the ingress-app1 and ingress-app2 Ingress resources listed. Wait for the ADDRESS column of the output to get its value, which should be the same as the IP address of your load balancer.

Setup Nginx Ingress Controller to Use Production-Ready SSL Certificates

You can set up the Nginx Ingress controller to use SSL certificates for your cluster services using an Issuer resource for CertManager. CertManager creates Custom Resource Definitions (CRDs) to handle the certificate issuance process from a Certificate Authority (CA) such as Let’s Encrypt, and these include the following:

  • Issuer: Defines an issuer configuration such as the type of issuer like ACME, method of certificate issuance like DNS01 or HTTP01, and the necessary credentials in a single namespace.
  • Cluster Issuer: Functions like Issuer but can issue certificates to any namespace within the Kubernetes cluster. It’s important when using the same issuer configuration across multiple namespaces.
  • Certificate: Defines a namespaced resource with the desired properties of a TLS/SSL certificate such as the domain names, associated secrets to store the certificate, and the trusted CA issuer to use. The certificate must reference an Issuer or Cluster Issuer resource.

In this section, you'll set up the Nginx Ingress Controller to use trusted SSL certificates issued by CertManager.

  1. Inspect the available CRDs by running the following command.

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

    Your output should contain resources related to CertManager, such as issuers.cert-manager.io, certificates.cert-manager.io, clusterissuers.cert-manager.io, and a few more.

Setup Let’s Encrypt Certificates

  1. Create a new issuer manifest. For example, cert-issuer.yaml.

    console
    $ nano cert-issuer.yaml
    
  2. Add the following configuration to the file.

    yaml
    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
    

    The above configuration uses the ACME issuer with the following fields:

    • email: Active email address to associate with the ACME account. Do not specify a @example.com email because it would not allow the issuer resource to become active.
    • server:: URL to access the ACME Let’s Encrypt server endpoint.
    • privateKeySecretRef: name: Kubernetes secret to store the generated ACME account private key.
    • solvers: Defines the certificate issuer challenge. You can use the http01 challenge or the dns01 challenge. It’s recommended to use the http01 challenge unless issuing wildcard certificates.

    Enter your email address to replace the pre-filled value, then save and close the file.

  3. Apply the issuer resource to your cluster.

    console
    $ kubectl apply -f cert-issuer.yaml
    
  4. Verify that the issuer resource is available and ready to use.

    console
    $ kubectl get issuer
    

    Your output should look like the one below.

    NAME                READY   AGE
    letsencrypt-nginx   False   4s

    The issuer's READY value is False. After some time, the status changes to True.

  5. To configure the Ingress controller to map hosts with TLS, follow the steps below:

  6. Check the state of your Ingress resources.

    console
    $ kubectl get ingress
    
  7. Edit the app1-ingress.yaml file you created earlier.

    console
    $ nano app1-ingress.yaml
    
  8. Add the following spec.tls section to the file:

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

    The manifest should look like:

    yaml
    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
    

    Save and close the file.

  9. Similarly, edit the app2-ingress.yaml file and add the following content to the manifest file.

    yaml
    tls:
      - hosts:
        - app1.example.com
        secretName: letsencrypt-nginx-app2
    
  10. Apply the configurations to enable TLS on each of the hosts.

    console
    $ kubectl apply -f app1-ingress.yaml
    
    console
    $ kubectl apply -f app2-ingress.yaml
    
  11. Verify that your Ingress resources have the TLS port 443 activated under the PORTS column.

    console
    $ kubectl get ingress
    

    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
  12. Verify that the certificate resources are available.

    console
    $ kubectl get certificates
    

    Your output should look like the one below.

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

    If the READY column returns True, your Let’s Encrypt certificates are successfully propagated. If False, please check your Ingress resource configuration and reapply changes to request a new certificate.

Test the SSL Configuration

Depending on your SSL certificate deployment method, verify that you can securely access your services over HTTPS using a web browser of your choice.

https://app1.example.com
https://app2.example.com

If you try to access your hosts over HTTP, the Ingress controller automatically redirects your request to HTTPS.

Import Commercial SSL Certificates

To import commercial SSL certificates purchased from a trusted certificate authority (CA), convert the certificate and private key to the base64 format, add them to a Kubernetes secret, and then configure your Ingress resources to use the certificates.

  1. Convert the commercial SSL certificate and private key to base64. Copy the resultant values to your clipboard.

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

    Replace /path with the actual directory path to your commercial SSL certificate and private key.

  2. Create a new Kubernetes secrets manifest.

    console
    $ nano ssl-secret.yaml
    
  3. Add the following configurations to the file.

    yaml
    apiVersion: v1
    kind: Secret
    metadata:
      name: prod-ssl-secret
    type: kubernetes.io/tls
    data:
      tls.crt: <paste-base64-values>
      tls.key: <paste-base64-values>
    

    Paste your base64 encoded values to the respective fields as follows:

    • tls-crt: SSL certificate in base64
    • tls.key: Certificate private key in base64

    Save and close the file.

  4. To configure Nginx to use your commercial SSL certificate, mention the above Secret resource name in your Ingress resource's spec.tls.secretName field.

Troubleshooting

  1. Nginx 502 Gateway Error:

    Verify that your Ingress configuration is correct and the referenced services are available and running.

  2. Unable to generate Let’s Encrypt Certificates, READY column remains False:

    • Verify that your Issuer and Certificate configurations are correct.
    • Provide a valid email address.
    • Check the Issuer resource Logs for further troubleshooting.

Conclusion

In this article, you have installed and set up the Nginx Ingress Controller with SSL on a Vultr Kubernetes Engine (VKE) cluster. You can use the controller to route external requests to cluster services securely over HTTPS.

Comments

No comments yet.