Securing Weaviate with SSL/TLS
Why security matters: Understanding the risks
Section titled “Why security matters: Understanding the risks”When your Weaviate database is exposed to the internet, unencrypted connections create serious vulnerabilities. Network packets can be intercepted at various points along their journey, exposing:
- Sensitive queries and search terms revealing what data you're working with
- Vector embeddings and data payloads containing your proprietary information
- Authentication credentials if transmitted without encryption
- API keys and tokens that could grant unauthorized access to your systems
That's exactly why it's important to secure your database using SSL/TLS (Secure Sockets Layer/Transport Layer Security), encryption protocols that create a secure, encrypted connection between your clients and your database. SSL/TLS ensures that even if network traffic is intercepted, the data remains unreadable to unauthorized parties.
This guide will take you through three different options for securing your self-hosted database. Whether you're a small startup or an enterprise giant, there's a solution for you!
When is SSL/TLS required?
Section titled “When is SSL/TLS required?”Although testing and local deployments might not necessitate network encryption, enabling SSL/TLS is considered a best practice for any production deployment, internal or external.
Understanding Weaviate's dual-port architecture
Section titled “Understanding Weaviate's dual-port architecture”Before we dive into implementation, it's important to understand that Weaviate requires two separate endpoints, both of which need SSL/TLS protection:
| Default port | Purpose | Protocol |
|---|---|---|
| 8080 | REST API and query interface (queries also available via gRPC) | HTTP |
| 50051 | High-performance data operations and queries | gRPC |
This dual-port requirement adds complexity compared to traditional single-port applications, as each endpoint needs its own domain/subdomain and SSL certificate.
Choosing your security approach
Section titled “Choosing your security approach”While the possibilities are endless for securing your vector database, this guide walks you through 3 approaches with varying levels of complexity, cost, and scalability. Here's how to choose:
| Approach | Complexity | Best for | Infrastructure | Key benefits |
|---|---|---|---|---|
| Option 1: Nginx Proxy Manager | Novice | Personal projects, GUI preference | Docker-based deployments | Easy GUI configuration, visual certificate management |
| Option 2: Traefik | Intermediate | Growing companies, IaC workflows | Docker Compose environments | Automated configuration, Infrastructure as Code, container-native |
| Option 3: Kubernetes + Cert-Manager | Advanced | Enterprise, production scale | Kubernetes clusters | Auto-renewal, high availability, enterprise-grade orchestration |
Option 1: Using Nginx Proxy Manager
Section titled “Option 1: Using Nginx Proxy Manager”- Complexity level: Novice
- Perfect for: Small business, teams who prefer ClickOps (GUI-based operations), or personal projects.
- Infrastructure: Docker and Docker Compose required. This approach is specifically for Docker-based deployments and is not compatible with Kubernetes.
What you'll need:
- A VPS or server with a public IP address
- A registered domain name with DNS management access
- Docker and Docker Compose installed
- Basic command-line knowledge
Step 1: Configure your DNS
Section titled “Step 1: Configure your DNS”- Before requesting SSL certificates, configure your DNS records:
A Record: weaviate.yourdomain.com → Your-Server-IP
A Record: grpc.yourdomain.com → Your-Server-IPMake sure to wait for DNS propagation before proceeding.
Step 2: Create your shared Docker network
Section titled “Step 2: Create your shared Docker network”- Create a network for secure container communication:
docker network create weaviate-networkStep 3: Deploy Weaviate
Section titled “Step 3: Deploy Weaviate”- Create a directory for your project:
mkdir ~/weaviate-ssl
cd ~/weaviate-ssl- Create a
docker-compose.ymlfile for Weaviate.
---
services:
weaviate:
image: cr.weaviate.io/semitechnologies/weaviate:1.38.2
container_name: weaviate
restart: unless-stopped
networks:
- weaviate-network
environment:
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: "false"
PERSISTENCE_DATA_PATH: "/var/lib/weaviate"
DEFAULT_VECTORIZER_MODULE: "none"
CLUSTER_HOSTNAME: "node1"
volumes:
- weaviate_data:/var/lib/weaviate
volumes:
weaviate_data:
networks:
weaviate-network:
external: true- Start Weaviate:
docker-compose up -d- Verify that it's running:
# Test from within the Docker network (this should work)
docker exec weaviate curl http://localhost:8080/v1/meta
# Test from host (this should fail - connection refused)
curl http://localhost:8080/v1/metaStep 4: Install Nginx Proxy Manager
Section titled “Step 4: Install Nginx Proxy Manager”- Create a separate directory:
mkdir ~/nginx-proxy-manager
cd ~/nginx-proxy-manager- Create a
docker-compose.ymlfile:
services:
app:
image: "jc21/nginx-proxy-manager:latest"
container_name: nginx-proxy-manager
restart: unless-stopped
networks:
- weaviate-network
ports:
- "8080:80" # HTTP
- "81:81" # Admin UI
- "443:443" # HTTPS
environment:
DB_SQLITE_FILE: "/data/database.sqlite"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
weaviate-network:
external: true- Start Nginx Proxy Manager
docker-compose up -dStep 5: Access the admin interface
Section titled “Step 5: Access the admin interface”- Navigate to
http://your-server-ip:81
Default credentials
Email: admin@example.com
Password: changemeStep 6: Secure the HTTP endpoint
Section titled “Step 6: Secure the HTTP endpoint”- Click Hosts -> Proxy hosts -> Add proxy host
Details Tab
Section titled “Details Tab”| Field | Value |
|---|---|
| Domain names | weaviate.yourdomain.com |
| Scheme | http |
| Forward Hostname/IP | weaviate |
| Forward Port | 8080 |
| Cache Assets | ✅ Checked |
| Block Common Exploits | ✅ Checked |
| Websockets Support | ✅ Checked |
SSL Tab
Section titled “SSL Tab”| Field | Value |
|---|---|
| SSL Certificate | Request a new SSL Certificate |
| Force SSL | ✅ Checked |
| HTTP/2 Support | ✅ Checked |
| HSTS Enabled | ✅ Checked |
| Email Address | your-email@example.com |
- Agree to the TOS.
- Click Save and wait 30-60 seconds for certificate provisioning.
Step 7: Secure the gRPC endpoints
Section titled “Step 7: Secure the gRPC endpoints”- Create a second proxy host for gRPC:
Details tab
| Field | Value |
|---|---|
| Domain Names | grpc.yourdomain.com |
| Scheme | http |
| Forward Hostname/IP | weaviate |
| Forward Port | 50051 |
| Block Common Exploits | ✅ Checked |
| Websockets Support | ✅ Checked |
SSL Tab Configure SSL the same way as the HTTP endpoint
Advanced Tab (Critical for gRPC)
Add this custom Nginx configuration:
grpc_pass grpc://localhost:50051;
grpc_set_header Host $host;Click Save.
Step 8: Test your secure connection
Section titled “Step 8: Test your secure connection”Test from command line
Section titled “Test from command line”# Test HTTP endpoint (this should work)
curl https://weaviate.yourdomain.com/v1/meta
# Test direct access (this should fail with a connection refused message)
curl http://your-server-ip:8080/v1/meta
# Verify SSL certificate
openssl s_client -connect weaviate.yourdomain.com:443 -servername weaviate.yourdomain.com < /dev/nullTest with Python client
Section titled “Test with Python client”import weaviate
# Connect to SSL-secured Weaviate
client = weaviate.connect_to_custom(
http_host="weaviate.yourdomain.com",
http_port=443,
http_secure=True,
grpc_host="grpc.yourdomain.com",
grpc_port=443,
grpc_secure=True
)
# Verify connection
print(f"✅ Connected: {client.is_ready()}")
client.close()Option 2: Using Traefik and Docker
Section titled “Option 2: Using Traefik and Docker”- Complexity level: Intermediate
- Perfect for: Growing companies, devs who love automation, and container-based infrastructure.
- Infrastructure: Docker and Docker Compose required. This approach uses Infrastructure as Code (IaC) principles and is designed for Docker environments, not Kubernetes.
This method introduces Infrastructure as Code (IaC) principles while remaining manageable. Traefik was built specifically for containerized environments. This approach is the perfect intermediary between the GUI of Option 1 and the pure Kubernetes of Option 3. You can think of this as the progression of learning another language: Option 1 was like using Duolingo or using a translation app, Option 2 is learning to speak the language without the use of a translation app, and Option 3 is becoming fluent.
What you'll need:
- Docker and Docker compose installed
- Domain name with DNS configured
- Understanding of Docker networking
- Familiarity with YAML configuration
Step 1: Prepare your environment
Section titled “Step 1: Prepare your environment”# Create Docker network
docker network create traefik-network
# Create project directory
mkdir -p ~/weaviate-traefik/{traefik,letsencrypt}
cd ~/weaviate-traefikStep 2: Configure DNS records
Section titled “Step 2: Configure DNS records”Before deploying, configure your DNS records to point to your server:
A Record: weaviate.yourdomain.com → Your-Server-IP
A Record: grpc.yourdomain.com → Your-Server-IPDNS propagation can take 5-60 minutes. Verify with:
dig weaviate.yourdomain.com
dig grpc.yourdomain.comWait for DNS propagation before starting the services to ensure Let's Encrypt can validate your domain.
Step 3: Configure Traefik
Section titled “Step 3: Configure Traefik”- Create
traefik/traefik.yml:
api:
dashboard: true
insecure: false
# Entry points (ports)
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
# Let's Encrypt configuration
certificatesResolvers:
letsencrypt:
acme:
email: your-email@example.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
# Docker provider
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: traefik-network
log:
level: INFOStep 4: Create Docker Compose configuration
Section titled “Step 4: Create Docker Compose configuration”- Create
docker-compose.yml:
networks:
traefik-network:
external: true
services:
traefik:
image: traefik:v2.10
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/traefik.yml:/traefik.yml:ro
- ./letsencrypt:/letsencrypt
networks:
- traefik-network
labels:
- "traefik.enable=true"
weaviate:
image: cr.weaviate.io/semitechnologies/weaviate:1.38.2
container_name: weaviate
restart: unless-stopped
environment:
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: "false"
PERSISTENCE_DATA_PATH: "/var/lib/weaviate"
DEFAULT_VECTORIZER_MODULE: "none"
CLUSTER_HOSTNAME: "node1"
volumes:
- weaviate_data:/var/lib/weaviate
networks:
- traefik-network
labels:
- "traefik.enable=true"
# HTTP/REST API endpoint
- "traefik.http.routers.weaviate-http.rule=Host(`weaviate.yourdomain.com`)"
- "traefik.http.routers.weaviate-http.entrypoints=websecure"
- "traefik.http.routers.weaviate-http.tls.certresolver=letsencrypt"
- "traefik.http.routers.weaviate-http.service=weaviate-http"
- "traefik.http.services.weaviate-http.loadbalancer.server.port=8080"
# gRPC endpoint
- "traefik.http.routers.weaviate-grpc.rule=Host(`grpc.yourdomain.com`)"
- "traefik.http.routers.weaviate-grpc.entrypoints=websecure"
- "traefik.http.routers.weaviate-grpc.tls.certresolver=letsencrypt"
- "traefik.http.routers.weaviate-grpc.service=weaviate-grpc"
- "traefik.http.services.weaviate-grpc.loadbalancer.server.port=50051"
- "traefik.http.services.weaviate-grpc.loadbalancer.server.scheme=h2c" # Enables HTTP/2 Cleartext for gRPC
volumes:
weaviate_data:Step 5: Set permissions and deploy
Section titled “Step 5: Set permissions and deploy”Before deploying, configure your DNS records to point to your server:
A Record: weaviate.yourdomain.com → Your-Server-IP
A Record: grpc.yourdomain.com → Your-Server-IPThe DNS propagation can take from 5-60 minutes, verify with:
dig weaviate.yourdomain.com
dig grpc.yourdomain.comWait for the DNS propagation before starting the services to ensure that Let's Encrypt can validate your domain.
# Set correct permissions for Let's Encrypt storage
touch ./letsencrypt/acme.json
chmod 600 ./letsencrypt/acme.json
# Start services
docker-compose up -d
# Monitor certificate acquisition
docker-compose logs -f traefikBe sure to check for messages indicating successful certificate issuance.
Step 6: Verify your deployment
Section titled “Step 6: Verify your deployment”# Check running containers
docker-compose ps
# View certificates in acme.json
cat ./letsencrypt/acme.json | jq '.letsencrypt.Certificates[].domain'
# Test HTTP endpoint
curl https://weaviate.yourdomain.com/v1/meta
# Verify SSL certificate
openssl s_client -connect weaviate.yourdomain.com:443 -servername weaviate.yourdomain.com < /dev/null 2>/dev/null | grep "Verify return code"Step 7: Connect with your application
Section titled “Step 7: Connect with your application”import weaviate
client = weaviate.connect_to_custom(
http_host="weaviate.yourdomain.com",
http_port=443,
http_secure=True,
grpc_host="grpc.yourdomain.com",
grpc_port=443,
grpc_secure=True
)
try:
print(f"✅ Connected: {client.is_ready()}")
# Test basic operation
collections = client.collections.list_all()
print(f"Collections: {collections}")
finally:
client.close()Option 3: Using Kubernetes and Cert-Manager
Section titled “Option 3: Using Kubernetes and Cert-Manager”- Complexity level: Advanced
- Perfect for: Scalable applications, production environments and production environments.
- Infrastructure: Kubernetes cluster required. This approach is exclusively for Kubernetes deployments and cannot be used with standalone Docker or Docker Compose setups.
What you'll need:
- Running Kubernetes cluster
- kubectl configured with cluster access
- Helm 3 installed
- Domain with ability to update DNS to LoadBalancer IP
- Strong Kubernetes knowledge
Step 1: Verify cluster access
Section titled “Step 1: Verify cluster access”# Check cluster info
kubectl cluster-info
# Verify you have proper permissions
kubectl auth can-i create namespace
# Check cluster version
kubectl versionStep 2: Install Cert-Manager
Section titled “Step 2: Install Cert-Manager”# Install Cert-Manager CRDs and controller
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
# Verify installation
kubectl get pods --namespace cert-manager
# Wait for all pods to be ready (may take 1-2 minutes)
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/instance=cert-manager \
-n cert-manager \
--timeout=300sStep 3: Install Traefik Ingress Controller
Section titled “Step 3: Install Traefik Ingress Controller”- Install Traefik:
# Add Traefik Helm repository
helm repo add traefik https://traefik.github.io/charts
helm repo update
# Create namespace
kubectl create namespace traefik
# Install Traefik with HTTPS redirect enabled (v34+ syntax)
helm install traefik traefik/traefik \
--namespace traefik \
--set ports.websecure.tls.enabled=true \
--set "additionalArguments={--entrypoints.web.http.redirections.entrypoint.to=websecure,--entrypoints.web.http.redirections.entrypoint.scheme=https}"
# Verify installation
kubectl get pods -n traefik
kubectl get svc -n traefik
# Get LoadBalancer IP (may take 1-5 minutes)
kubectl get svc -n traefik traefik -wStep 4: Create Let's Encrypt ClusterIssuer
Section titled “Step 4: Create Let's Encrypt ClusterIssuer”- Create
letsencrypt-issuer.yaml:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# Let's Encrypt production server
server: https://acme-v02.api.letsencrypt.org/directory
# Email for renewal and security notices
email: your-email@example.com
# Secret to store ACME account private key
privateKeySecretRef:
name: letsencrypt-prod-account-key
# HTTP-01 challenge
solvers:
- http01:
ingress:
class: traefik- Apply the issuer
kubectl apply -f letsencrypt-issuer.yaml
# Verify issuer is ready
kubectl get clusterissuer
kubectl describe clusterissuer letsencrypt-prodStep 5: Deploy Weaviate
Section titled “Step 5: Deploy Weaviate”- Create
weaviate-deployment.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: weaviate
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: weaviate-data
namespace: weaviate
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: weaviate
namespace: weaviate
labels:
app: weaviate
spec:
replicas: 1
selector:
matchLabels:
app: weaviate
template:
metadata:
labels:
app: weaviate
spec:
containers:
- name: weaviate
image: cr.weaviate.io/semitechnologies/weaviate:1.38.2
ports:
- containerPort: 8080
name: http
- containerPort: 50051
name: grpc
env:
- name: QUERY_DEFAULTS_LIMIT
value: "25"
- name: AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED
value: "false"
- name: PERSISTENCE_DATA_PATH
value: "/var/lib/weaviate"
- name: DEFAULT_VECTORIZER_MODULE
value: "none"
- name: CLUSTER_HOSTNAME
value: "node1"
volumeMounts:
- name: weaviate-data
mountPath: /var/lib/weaviate
resources:
requests:
memory: "2Gi"
cpu: "1000m"
limits:
memory: "4Gi"
cpu: "2000m"
livenessProbe:
httpGet:
path: /v1/.well-known/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /v1/.well-known/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: weaviate-data
persistentVolumeClaim:
claimName: weaviate-data
---
apiVersion: v1
kind: Service
metadata:
name: weaviate
namespace: weaviate
labels:
app: weaviate
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
name: http
- port: 50051
targetPort: 50051
name: grpc
selector:
app: weaviate- Deploy Weaviate:
kubectl apply -f weaviate-deployment.yaml
# Verify deployment
kubectl get pods -n weaviate
kubectl get svc -n weaviate
# Wait for pod to be ready
kubectl wait --for=condition=ready pod \
-l app=weaviate \
-n weaviate \
--timeout=300s
# Check logs
kubectl logs -n weaviate -l app=weaviate --tail=50Step 6: Create Ingress with TLS
Section titled “Step 6: Create Ingress with TLS”- Create
weaviate-ingressroute.yaml:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: weaviate-http-cert
namespace: weaviate
spec:
secretName: weaviate-http-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- weaviate.yourdomain.com
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: weaviate-grpc-cert
namespace: weaviate
spec:
secretName: weaviate-grpc-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- grpc.yourdomain.com
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: weaviate-http
namespace: weaviate
spec:
entryPoints:
- websecure
routes:
- match: Host(`weaviate.yourdomain.com`)
kind: Rule
services:
- name: weaviate
port: 8080
tls:
secretName: weaviate-http-tls
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: weaviate-grpc
namespace: weaviate
spec:
entryPoints:
- websecure
routes:
- match: Host(`grpc.yourdomain.com`)
kind: Rule
services:
- name: weaviate
port: 50051
scheme: h2c # HTTP/2 Cleartext for gRPC
tls:
secretName: weaviate-grpc-tls- Apply the configuration:
kubectl apply -f weaviate-ingressroute.yaml
# Verify IngressRoute creation
kubectl get ingressroute -n weaviate
# Watch certificate status
kubectl get certificate -n weaviate -w
# Verify certificates are issued
kubectl describe certificate weaviate-http-cert -n weaviate
kubectl describe certificate weaviate-grpc-cert -n weaviateStep 7: Verify your SSL certificates
Section titled “Step 7: Verify your SSL certificates”# Check certificate details
kubectl describe certificate weaviate-http-tls -n weaviate
# View the actual certificate
kubectl get secret weaviate-http-tls -n weaviate -o jsonpath='{.data.tls\.crt}' | \
base64 -d | \
openssl x509 -text -noout
# Check expiration date
kubectl get secret weaviate-http-tls -n weaviate -o jsonpath='{.data.tls\.crt}' | \
base64 -d | \
openssl x509 -noout -enddateStep 8: Test your secure Weaviate
Section titled “Step 8: Test your secure Weaviate”import weaviate
# Connect to Kubernetes-hosted Weaviate
client = weaviate.connect_to_custom(
http_host="weaviate.yourdomain.com",
http_port=443,
http_secure=True,
grpc_host="grpc.yourdomain.com",
grpc_port=443,
grpc_secure=True
)
try:
print(f"✅ Connected: {client.is_ready()}")
# Get metadata
meta = client.get_meta()
print(f"Weaviate version: {meta.get('version')}")
finally:
client.close()Questions and feedback
Section titled “Questions and feedback”Have a question or feedback? Here's how to reach us.