---
title: "Configure a Custom SSL Certificate for a Kubernetes Load Balancer | Hostman Docs"
description: "Learn how to configure a custom SSL certificate on a Hostman Kubernetes load balancer using a TLS secret. Includes step-by-step setup, verification, and certificate rotation."
---

> For the complete documentation index for AI agents, see [llms.txt](https://hostman.com/llms.txt).

The Hostman load balancer supports custom SSL certificates stored as Kubernetes secrets of type `kubernetes.io/tls`. To enable HTTPS with your own certificate, create a TLS secret containing your certificate and private key, then reference it in the load balancer's annotations.

> [!NOTE]
> Self-signed certificates are not supported. You must use a certificate issued by a trusted Certificate Authority (CA).

## Create a TLS Secret

1.  Download your certificate files from your CA.
    
2.  Create a TLS secret in the same namespace as your load balancer:
    

```shell
kubectl -n <namespace> create secret tls my-app-tls \
  --cert=crt.crt \
  --key=key.key
```

3.  Verify the secret type:
    

```shell
kubectl -n <namespace> get secret my-app-tls -o yaml
```

The output should include:

```shell
type: kubernetes.io/tls
```

## Configure the Service

Add the following annotations to your load balancer manifest:

```yaml
apiVersion: v1
kind: Service
metadata:
  name: my-app
  annotations:
    k8s.hostman.com/attached-loadbalancer-ssl: "true"
    k8s.hostman.com/attached-loadbalancer-ssl-type: "custom"
    k8s.hostman.com/attached-loadbalancer-ssl-fqdn: "example.com"
    k8s.hostman.com/attached-loadbalancer-ssl-secret-name: "my-app-tls"
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - name: https
      port: 443
      targetPort: 80
      appProtocol: k8s.hostman.com/proto-https
```

Annotation reference:

-   -   `k8s.hostman.com/attached-loadbalancer-ssl`: Enables SSL certificate support.
        
    -   `k8s.hostman.com/attached-loadbalancer-ssl-type`: Sets the certificate type. Use `custom` for your own certificate.
        
    -   `k8s.hostman.com/attached-loadbalancer-ssl-fqdn`: The domain name the certificate was issued for.
        
    -   `k8s.hostman.com/attached-loadbalancer-ssl-secret-name`: The name of the TLS secret containing the certificate and private key.
        

4.  Apply the changes:
    

```shell
kubectl apply -f service.yaml
```

## Verify the Certificate

Run the following command to check which certificate the load balancer is serving:

```shell
echo | openssl s_client \
  -connect example.com:443 \
  -servername example.com \
  2>/dev/null | openssl x509 -noout -subject -issuer -dates
```

A successful result looks like this:

```shell
subject=CN=example.com
issuer=C=BE, O=GlobalSign nv-sa, CN=GlobalSign GCC R6 AlphaSSL CA 2025
```

You can also verify the HTTPS connection using `curl`:

```shell
curl -kv https://example.com
```

Look for the following in the output:

```shell
SSL certificate verify ok.
```

And confirm your certificate details appear:

```shell
subject: CN=example.com
```

If you see the following instead:

```shell
subject: CN=empty
```

That means the load balancer could not apply your certificate and has fallen back to the default one. Double-check your secret name and annotations.

## Rotate the Certificate

To replace an existing certificate, update the secret in place:

```shell
kubectl -n <namespace> create secret tls my-app-tls \
  --cert=new.crt \
  --key=new.key \
  --dry-run=client -o yaml | kubectl apply -f -
```

After updating the secret, trigger a load balancer reconfiguration by touching any service annotation:

```shell
kubectl -n <namespace> annotate svc my-app \
  k8s.hostman.com/rotate-ts="$(date +%s)" \
  --overwrite
```

The Cloud Controller Manager (CCM) will re-read the secret and push the updated certificate to the load balancer.

## Practical Example

This example walks you through configuring a custom SSL certificate on a test Nginx application.

Prerequisites: a domain name and a valid certificate issued by a trusted CA.

### Create a Namespace

Create a dedicated namespace for testing:

```shell
kubectl create namespace test-namespace
```

### Deploy the Test Application

1.  Create a file called `nginx-deployment.yaml`:
    

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: test-namespace
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-custom-cert-test
  template:
    metadata:
      labels:
        app: nginx-custom-cert-test
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80
```

2.  Apply the manifest:
    

```shell
kubectl apply -f nginx-deployment.yaml
```

3.  Confirm the pod is running:
    

```shell
kubectl get pods -n test-namespace
```

### Create a Load Balancer

1.  Create a file called `nginx-loadbalancer.yaml`:
    

```yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-loadbalancer
  namespace: test-namespace
spec:
  type: LoadBalancer
  selector:
    app: nginx-custom-cert-test
  ports:
    - name: https
      port: 443
      targetPort: 80
      appProtocol: k8s.hostman.com/proto-https
```

2.  Apply the manifest:
    

```shell
kubectl apply -f nginx-loadbalancer.yaml
```

3.  Wait for an external IP address to be assigned:
    

```shell
kubectl get svc nginx-loadbalancer -n test-namespace -w
```

Example output:

```shell
NAME                 TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)
nginx-loadbalancer   LoadBalancer   10.101.130.253   203.0.113.10    443:31826/TCP
```

### Configure DNS

1.  Create an A record for your domain pointing to the load balancer's external IP.
    
2.  Confirm the domain resolves correctly:
    

```shell
dig +short example.com
```

The output should return the load balancer's IP address.

### Create the TLS Secret

1.  Create the TLS secret in the same namespace as your service:
    

```shell
kubectl -n test-namespace create secret tls my-app-tls \
  --cert=crt.crt \
  --key=key.key
```

2.  Verify the secret was created correctly:
    

```shell
kubectl -n test-namespace get secret my-app-tls -o yaml
```

The output should include:

```shell
type: kubernetes.io/tls
```

3.  Optionally, inspect the certificate directly from the secret:
    

```shell
kubectl -n test-namespace get secret my-app-tls \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d \
  | openssl x509 -noout -subject -issuer -dates
```

### Apply the Certificate to the Load Balancer

1.  Add the SSL annotations to your `nginx-loadbalancer.yaml` manifest:
    

```yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-loadbalancer
  namespace: test-namespace
  annotations:
    k8s.hostman.com/attached-loadbalancer-ssl: "true"
    k8s.hostman.com/attached-loadbalancer-ssl-type: "custom"
    k8s.hostman.com/attached-loadbalancer-ssl-fqdn: "example.com"
    k8s.hostman.com/attached-loadbalancer-ssl-secret-name: "my-app-tls"
spec:
  type: LoadBalancer
  selector:
    app: nginx-custom-cert-test
  ports:
    - name: https
      port: 443
      targetPort: 80
      appProtocol: k8s.hostman.com/proto-https
```

2.  Apply the updated manifest:
    

```shell
kubectl apply -f nginx-loadbalancer.yaml
```

3.  Verify the service annotations are set:
    

```shell
kubectl -n test-namespace get svc nginx-loadbalancer -o yaml
```

The annotations section should include:

```shell
k8s.hostman.com/attached-loadbalancer-ssl: "true"
k8s.hostman.com/attached-loadbalancer-ssl-type: custom
k8s.hostman.com/attached-loadbalancer-ssl-fqdn: example.com
k8s.hostman.com/attached-loadbalancer-ssl-secret-name: my-app-tls
```

Once the CCM has successfully read the certificate, a hash annotation will appear:

```shell
k8s.hostman.com/attached-loadbalancer-ssl-cert-hash: ...
```

This confirms the certificate has been picked up from the secret.

### Verify the Application

Open your domain in a browser. If the certificate was applied correctly, the browser will establish a secure HTTPS connection without any warnings.

You can also test with `curl`:

```shell
curl -I https://example.com
```

A successful response confirms that:

-   the certificate has been loaded onto the load balancer;
    
-   the HTTPS connection is established correctly;
    
-   the load balancer is forwarding requests to your application running in Kubernetes.
    

### Clean Up

When you're done testing, delete the namespace to remove all associated resources, including the Deployment, Service, and TLS secret:

```shell
kubectl delete namespace test-namespace
```
