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.
$ helm repo update
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
$ nano app1-deploy.yaml
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
$ nano app2-deploy.yaml
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:
$ kubectl get deployments
Create Service Manifests
Service for App1
$ nano app1-svc.yaml
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
$ nano app2-svc.yaml
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:
$ kubectl get services
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
$ nano app1-ingress.yaml
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
$ nano app2-ingress.yaml
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:
$ kubectl get ingress
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.
$ nano cert-issuer.yaml
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.comas 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
http01method 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:
$ kubectl get issuer
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:
$ kubectl get ingress
Edit the app1-ingress.yaml file to include a TLS section:
$ nano app1-ingress.yaml
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:
$ kubectl get ingress
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:
$ nano ssl-secret.yaml
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.


