You are looking at the documentation of a prior release. To read the documentation of the latest release, please visit here.

New to KubeDB? Please start here.

Configure TLS/SSL for Milvus

This guide will show you how to deploy a Milvus database with TLS/SSL enabled from the start, using cert-manager.

Before You Begin

  • Install cert-manager in your cluster.

  • You should be familiar with the following KubeDB concepts:

  • Complete the dependency setup from Prepare Dependencies. It installs MinIO, creates the my-release-minio secret, and installs the etcd operator required by Milvus.

Note: The yaml files used in this tutorial are stored in docs/guides/milvus/tls/yamls folder in GitHub repository kubedb/docs.

Create a cert-manager Issuer

KubeDB uses cert-manager to issue the Milvus certificates. First create a self-signed CA secret, then an Issuer (or ClusterIssuer) backed by it.

$ openssl genrsa -out ca.key 2048
$ openssl req -x509 -new -nodes -key ca.key -subj "/CN=milvus-ca/O=kubedb" -days 3650 -out ca.crt
$ kubectl create secret tls milvus-ca --cert=ca.crt --key=ca.key -n demo
secret/milvus-ca created

issuer.yaml

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: milvus-issuer
  namespace: demo
spec:
  ca:
    secretName: milvus-ca
$ kubectl apply -f https://github.com/kubedb/docs/raw/v2026.7.10/docs/guides/milvus/tls/yamls/issuer.yaml
issuer.cert-manager.io/milvus-issuer created

$ kubectl get issuer -n demo
NAME            READY   AGE
milvus-issuer   True    5s

A ClusterIssuer works the same way; a sample cluster-issuer.yaml (backed by secret milvus-cluster-ca) is included in the yamls folder. With a ClusterIssuer, set spec.tls.issuerRef.kind: ClusterIssuer.

Deploy a TLS-Secured Standalone Milvus

The standalone manifest enables TLS via spec.tls:

apiVersion: kubedb.com/v1alpha2
kind: Milvus
metadata:
  name: milvus-standalone
  namespace: demo
spec:
  version: "2.6.11"
  topology:
    mode: Standalone
  objectStorage:
    configSecret:
      name: "my-release-minio"
  storageType: Durable
  storage:
    accessModes:
      - ReadWriteOnce
    storageClassName: local-path
    resources:
      requests:
        storage: 1Gi
  tls:
    issuerRef:
      name: milvus-issuer
      kind: Issuer
      apiGroup: "cert-manager.io"
    external:
      mode: mTLS
    internal:
      mode: TLS
  • spec.tls.external.mode: mTLS requires clients to present a certificate.
  • spec.tls.internal.mode: TLS encrypts inter-component traffic.
$ kubectl apply -f https://github.com/kubedb/docs/raw/v2026.7.10/docs/guides/milvus/tls/yamls/standalone.yaml
milvus.kubedb.com/milvus-standalone created

Wait until it is Ready.

Verify TLS

Certificate Secrets

KubeDB requests the server and client certificates and stores them in secrets:

$ kubectl get secret -n demo | grep -E 'milvus-standalone-(server|client)-cert'
milvus-standalone-client-cert   kubernetes.io/tls   4   91s
milvus-standalone-server-cert   kubernetes.io/tls   3   91s

TLS Files Mounted in the Pod

The certificates and CA are mounted at /milvus/tls:

$ kubectl exec -n demo milvus-standalone-0 -c milvus -- ls -l /milvus/tls
ca.pem
client.key
client.pem
server.key
server.pem

Rendered Configuration

The rendered milvus.yaml wires the certificates into Milvus:

$ kubectl get secret <config-secret> -n demo -o jsonpath='{.data.milvus\.yaml}' | base64 -d | grep -A4 internaltls
internaltls:
    caPemPath: /milvus/tls/ca.pem
    serverKeyPath: /milvus/tls/server.key
    serverPemPath: /milvus/tls/server.pem
    sni: milvus-standalone

AppBinding Scheme

Because TLS is enabled, the AppBinding connection scheme is https:

$ kubectl get appbinding milvus-standalone -n demo -o jsonpath='{.spec.clientConfig.service.scheme}'
https

Connect Through External mTLS

This guide uses spec.tls.external.mode: mTLS, so clients must present the client certificate.

Extract the CA and client certificate

$ kubectl get secret milvus-standalone-server-cert -n demo \
    -o jsonpath='{.data.ca\.crt}' | base64 -d > /tmp/ca.crt

$ kubectl get secret milvus-standalone-client-cert -n demo \
    -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/client.crt

$ kubectl get secret milvus-standalone-client-cert -n demo \
    -o jsonpath='{.data.tls\.key}' | base64 -d > /tmp/client.key

$ PASSWORD=$(kubectl get secret milvus-standalone-auth -n demo -o jsonpath='{.data.password}' | base64 -d)

Port-forward the REST port

Run this in a separate terminal:

$ kubectl port-forward svc/milvus-standalone -n demo 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080

Create a collection over HTTPS

$ curl -s -X POST "https://localhost:8080/v2/vectordb/collections/create" \
    --cacert /tmp/ca.crt \
    --cert /tmp/client.crt \
    --key /tmp/client.key \
    -H "Authorization: Bearer root:${PASSWORD}" \
    -H "Content-Type: application/json" \
    -d '{
      "collectionName": "tls_test_collection",
      "dbName": "default",
      "dimension": 4,
      "metricType": "L2"
    }' | jq .

Insert and search sample vectors

$ curl -s -X POST "https://localhost:8080/v2/vectordb/entities/insert" \
    --cacert /tmp/ca.crt \
    --cert /tmp/client.crt \
    --key /tmp/client.key \
    -H "Authorization: Bearer root:${PASSWORD}" \
    -H "Content-Type: application/json" \
    -d '{
      "collectionName": "tls_test_collection",
      "dbName": "default",
      "data": [
        {"id": 1, "vector": [0.9, 0.8, 0.7, 0.6]},
        {"id": 2, "vector": [0.5, 0.4, 0.3, 0.2]}
      ]
    }' | jq .

$ curl -s -X POST "https://localhost:8080/v2/vectordb/entities/search" \
    --cacert /tmp/ca.crt \
    --cert /tmp/client.crt \
    --key /tmp/client.key \
    -H "Authorization: Bearer root:${PASSWORD}" \
    -H "Content-Type: application/json" \
    -d '{
      "collectionName": "tls_test_collection",
      "dbName": "default",
      "data": [[0.5, 0.4, 0.3, 0.2]],
      "topK": 2,
      "metricType": "L2"
    }' | jq .

If you omit --cert and --key, the request should fail because client certificate authentication is required.

TLS-Secured Distributed Milvus

The distributed manifest enables TLS the same way. The server/client certificates are issued once and propagated to every distributed role (mixcoord, datanode, querynode, streamingnode, proxy).

apiVersion: kubedb.com/v1alpha2
kind: Milvus
metadata:
  name: milvus-cluster
  namespace: demo
spec:
  version: "2.6.11"
  objectStorage:
    configSecret:
      name: "my-release-minio"
  topology:
    mode: Distributed
    distributed:
      streamingnode:
        storageType: Durable
        storage:
          accessModes:
            - ReadWriteOnce
          storageClassName: local-path
          resources:
            requests:
              storage: 1Gi
  tls:
    issuerRef:
      name: milvus-issuer
      kind: Issuer
      apiGroup: "cert-manager.io"
    external:
      mode: mTLS
    internal:
      mode: TLS

After it becomes Ready, the certificate secrets exist, the certificates are mounted into every role’s pods, and the AppBinding scheme is https:

$ kubectl get secret -n demo | grep -E 'milvus-cluster-(server|client)-cert'
milvus-cluster-client-cert   kubernetes.io/tls   4   4m
milvus-cluster-server-cert   kubernetes.io/tls   3   4m

$ kubectl exec -n demo milvus-cluster-mixcoord-0 -c milvus -- ls /milvus/tls
ca.pem
client.key
client.pem
server.key
server.pem

$ kubectl get appbinding milvus-cluster -n demo -o jsonpath='{.spec.clientConfig.service.scheme}'
https

Verify internal TLS between distributed roles

You can verify inter-component TLS from the proxy pod:

$ kubectl exec -it milvus-cluster-proxy-0 -n demo -- \
    openssl s_client \
    -connect milvus-cluster-querynode.demo.svc.cluster.local:21123 \
    -CAfile /milvus/tls/ca.pem \
    -servername milvus-cluster \
    -brief

You should see a successful TLS handshake.

Cleaning up

$ kubectl delete milvus.kubedb.com -n demo milvus-standalone
$ kubectl delete issuer -n demo milvus-issuer
$ kubectl delete secret -n demo milvus-ca
$ kubectl delete ns demo

Next Steps