Kubernetes
Certificates
Installation
Security
DevOps

Installing certificates on Kubernetes

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Installing certificates on Kubernetes usually means one of two things: terminating HTTPS at an ingress, or mounting certificate material into workloads for internal TLS or mutual TLS. The mechanics are straightforward, but the operational choice matters more than the YAML. You need to decide who issues the certificate, where it is stored, and how renewal will happen before you start applying manifests.

Choose the Certificate Flow First

In practice, Kubernetes certificate setups usually fall into three patterns:

  • a manually created TLS Secret
  • automated issuance and renewal with cert-manager
  • cloud or platform-managed certificates integrated with an ingress controller

Manual secrets are acceptable for development or tightly controlled internal systems. For anything public or long-lived, automated renewal is safer because certificate expiration is an operational failure, not just a configuration mistake.

Installing a Manual TLS Secret

If you already have a certificate and private key, Kubernetes expects them in a secret of type kubernetes.io/tls.

bash
1kubectl create secret tls web-tls \
2  --cert=./tls.crt \
3  --key=./tls.key \
4  -n web

The equivalent manifest looks like this:

yaml
1apiVersion: v1
2kind: Secret
3metadata:
4  name: web-tls
5  namespace: web
6type: kubernetes.io/tls
7data:
8  tls.crt: BASE64_CERT
9  tls.key: BASE64_KEY

The key names matter. For this secret type, controllers expect tls.crt and tls.key. Putting different keys in the same secret may work for custom code, but it will not behave like a standard TLS secret.

Attaching the Certificate to an Ingress

The most common use of a certificate in Kubernetes is ingress termination. The ingress resource points to the TLS secret and lists the hostnames it covers.

yaml
1apiVersion: networking.k8s.io/v1
2kind: Ingress
3metadata:
4  name: web-ingress
5  namespace: web
6spec:
7  ingressClassName: nginx
8  tls:
9    - hosts:
10        - app.example.com
11      secretName: web-tls
12  rules:
13    - host: app.example.com
14      http:
15        paths:
16          - path: /
17            pathType: Prefix
18            backend:
19              service:
20                name: web-service
21                port:
22                  number: 80

After applying the ingress, verify both the Kubernetes side and the DNS side. If the hostname does not resolve to the ingress endpoint, the certificate can be correct and HTTPS will still appear broken from the client perspective.

Mounting Certificates Into Pods

Some applications need the certificate files inside the container, not only at ingress. In that case, mount the secret as a volume.

yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4  name: api
5  namespace: web
6spec:
7  replicas: 1
8  selector:
9    matchLabels:
10      app: api
11  template:
12    metadata:
13      labels:
14        app: api
15    spec:
16      containers:
17        - name: api
18          image: nginx:1.27
19          volumeMounts:
20            - name: tls
21              mountPath: /etc/tls
22              readOnly: true
23      volumes:
24        - name: tls
25          secret:
26            secretName: web-tls

The mounted files are usually named after the secret keys, so the container sees /etc/tls/tls.crt and /etc/tls/tls.key.

Automating With cert-manager

For production clusters, cert-manager is the usual answer. It handles issuance and renewal by creating or refreshing the TLS secret on your behalf. A typical setup uses an issuer plus a certificate resource.

yaml
1apiVersion: cert-manager.io/v1
2kind: ClusterIssuer
3metadata:
4  name: letsencrypt-prod
5spec:
6  acme:
7    server: https://acme-v02.api.letsencrypt.org/directory
8    email: [email protected]
9    privateKeySecretRef:
10      name: letsencrypt-prod-account-key
11    solvers:
12      - http01:
13          ingress:
14            class: nginx
15---
16apiVersion: cert-manager.io/v1
17kind: Certificate
18metadata:
19  name: web-cert
20  namespace: web
21spec:
22  secretName: web-tls
23  issuerRef:
24    name: letsencrypt-prod
25    kind: ClusterIssuer
26  dnsNames:
27    - app.example.com

Once this is working, your ingress can keep referencing web-tls while cert-manager refreshes the contents as certificates rotate.

Verifying and Operating the Setup

A valid-looking manifest is not enough. Verify the deployed state.

bash
1kubectl describe secret web-tls -n web
2kubectl describe ingress web-ingress -n web
3kubectl get certificate -A
4curl -vk https://app.example.com/

For internal certificates mounted into pods, also verify the application process actually reloads or rereads the certificate after renewal. Some servers need a restart or a reload signal before they use the new files.

Common Pitfalls

The most frequent problem is putting the TLS secret in the wrong namespace. Ingress objects can only reference secrets in their own namespace.

Another issue is assuming a certificate is installed just because the secret exists. The ingress controller, DNS record, and certificate hostname must all align.

Teams also forget rotation. Manual certificate installation is easy on day one and painful on the day it expires. If the environment is long-lived, decide on renewal and alerting immediately.

Finally, avoid storing raw private keys in source control. Create secrets from secure files or a secret-management workflow, not from committed key material.

Summary

  • Decide first whether certificates are manual, automated, or platform-managed.
  • Use kubernetes.io/tls secrets with tls.crt and tls.key for standard TLS integration.
  • Reference the TLS secret from ingress for HTTPS termination.
  • Mount the secret into pods when workloads need certificate files directly.
  • Verify DNS, namespace, and renewal behavior instead of stopping at a successful kubectl apply.

Course illustration
Course illustration

All Rights Reserved.