This error occurs when an Ingress resource references a TLS secret that doesn't exist or is misconfigured. Fix it by creating the TLS secret in the correct namespace with proper certificate format, or configure cert-manager for automatic certificate management.
The "TLS secret not found" error indicates that the Ingress controller cannot locate the TLS secret referenced in your Ingress resource's tls.secretName field. Without this secret, the Ingress cannot serve HTTPS traffic and may fall back to serving a self-signed certificate or fail entirely. TLS secrets in Kubernetes must be of type kubernetes.io/tls and contain two keys: tls.crt (the certificate chain) and tls.key (the private key). The secret must exist in the same namespace as the Ingress resource—Kubernetes doesn't allow cross-namespace secret references for Ingress TLS. This error commonly occurs after cluster migrations, when secrets are accidentally deleted, or when using cert-manager before certificates are issued.
Check if the TLS secret exists in the same namespace as the Ingress:
# Check if secret exists
kubectl get secret <tls-secret-name> -n <namespace>
# Describe secret to verify type and keys
kubectl describe secret <tls-secret-name> -n <namespace>Expected output should show:
- Type: kubernetes.io/tls
- Data keys: tls.crt, tls.key
Create the secret from your certificate and key files:
# Create TLS secret
kubectl create secret tls my-tls-secret \
--cert=server.crt \
--key=server.key \
--namespace <namespace>
# Verify secret was created correctly
kubectl get secret my-tls-secret -n <namespace> -o jsonpath='{.type}'
# Should output: kubernetes.io/tlsFor self-signed certificates (development only):
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout server.key -out server.crt \
-subj "/CN=example.com/O=example.com" \
-addext "subjectAltName = DNS:example.com"
kubectl create secret tls my-tls-secret \
--cert=server.crt --key=server.key -n <namespace>If using intermediate certificates, ensure correct chain order:
# Correct order: leaf certificate first, then intermediates, then root
cat server.crt intermediate.crt root.crt > fullchain.pem
# Recreate secret with full chain
kubectl delete secret my-tls-secret -n <namespace>
kubectl create secret tls my-tls-secret \
--cert=fullchain.pem \
--key=server.key \
--namespace <namespace>Verify certificate details:
kubectl get secret my-tls-secret -n <namespace> \
-o jsonpath='{.data.tls\.crt}' | base64 -d | \
openssl x509 -text -nooutEnsure Ingress TLS hosts match rules hosts:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
namespace: <namespace>
spec:
ingressClassName: nginx
tls:
- hosts:
- example.com # Must match rules host
- www.example.com
secretName: my-tls-secret
rules:
- host: example.com # Must match tls hosts
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80Apply and verify:
kubectl apply -f ingress.yaml
kubectl describe ingress my-ingress -n <namespace>Install cert-manager and configure Let's Encrypt:
# Install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yamlCreate ClusterIssuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginxAdd annotation to Ingress:
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prodcert-manager will automatically create and renew the TLS secret.
Verify certificate is being served correctly:
# Check ingress controller logs
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller | grep -i tls
# Test TLS connection
curl -kv https://<ingress-ip> -H "Host: example.com"
# Verify certificate matches private key
kubectl get secret my-tls-secret -n <namespace> \
-o jsonpath='{.data.tls\.crt}' | base64 -d | \
openssl x509 -noout -modulus | openssl md5
kubectl get secret my-tls-secret -n <namespace> \
-o jsonpath='{.data.tls\.key}' | base64 -d | \
openssl rsa -noout -modulus | openssl md5
# Both MD5 hashes should matchcert-manager Best Practices:
- Always test with Let's Encrypt staging first: https://acme-staging-v02.api.letsencrypt.org/directory
- Use a monitored email address for renewal notifications
- Let cert-manager manage secrets; avoid manual modifications
- Monitor Certificate status: kubectl describe certificate <name>
Common cert-manager Issues:
- Incorrect ACME URL (use acme-v02, not acme-v2)
- Challenge failures—ensure domain resolves to cluster IP
- Rate limiting—Let's Encrypt has strict limits; use staging for testing
- Secret name conflicts—use unique names for Ingress TLS vs issuer privateKeySecretRef
Namespace Isolation: TLS secrets must be in the same namespace as the Ingress. Cross-namespace references are not supported. For multi-namespace setups, either create secrets in each namespace or use cert-manager's Certificate resource per namespace.
Failed to connect to server: connection refused (HTTP/2)
How to fix "HTTP/2 connection refused" error in Kubernetes
missing request for cpu in container
How to fix "missing request for cpu in container" in Kubernetes HPA
error: invalid configuration
How to fix "error: invalid configuration" in Kubernetes
etcdserver: cluster ID mismatch
How to fix "etcdserver: cluster ID mismatch" in Kubernetes
running with swap on is not supported
How to fix "running with swap on is not supported" in kubeadm