AI

Install Weaviate Vector Database on Ubuntu 26.04 / 24.04

A vector database only earns its place once your application can reach it, authenticate against it, and get back the right neighbours. This guide covers how to install Weaviate on Ubuntu 26.04 and 24.04, run it under Docker with an API key in front, and connect a Python client that stores vectors and searches them. Weaviate is the open source engine at the centre. Everything else here is the wiring around it.

Original content from computingforgeeks.com - post 170313

We deploy Weaviate standalone with Docker Compose, lock it down with API-key authentication, confirm the REST and gRPC endpoints answer, then use the Weaviate Python client to create a collection, insert objects with their own vectors, and run a near-vector search. A reverse proxy with HTTPS puts it safely on your network at the end.

Confirmed working on Ubuntu 26.04 (Weaviate 1.38.6, Docker 29.6) in July 2026. The same commands work on Ubuntu 24.04.

How Weaviate fits together

The standalone deployment is a single container, which keeps the moving parts easy to reason about. There are four pieces to know before we start:

  • The Weaviate server: one Go binary in a container. It serves a REST API on port 8080 and a gRPC API on port 50051. The Python client talks REST for schema work and gRPC for the heavy calls, batch inserts and search, so both ports matter.
  • Persistent storage: a Docker volume mounted at /var/lib/weaviate. Your collections and vectors survive a container restart because the data lives in the volume, not the container.
  • Where the vectors come from: two options. Weaviate can call a vectorizer module (the text2vec-* family) to embed your text for you, or you bring your own vectors from a model you run yourself. This guide uses self-provided vectors, so there is no external API key and no extra model container to manage. You can enable a module later without recreating the server.
  • The client: official libraries for Python, JavaScript/TypeScript, Go, and Java. We use the Python client, which is the one most people reach for when they wire Weaviate into a retrieval pipeline.

For high availability and horizontal scale, Weaviate runs as a multi-node cluster on Kubernetes. The single-container setup below is what most self-hosters actually need, and it is the right place to learn the API before you reach for a cluster.

Prerequisites

  • An Ubuntu 26.04 or 24.04 server with a sudo user.
  • RAM sized to your working set. RAM is the real sizing driver for a vector database because Weaviate keeps the HNSW graph in memory. A rough figure is vectors times dimensions times four bytes plus overhead, so a million 768-dimension vectors is roughly 3 GB for the index alone, before object data and headroom. A production workload often lands between 8 and 32 GB. The lab for this guide ran on 2 vCPU and 4 GB, which is a floor for following along, not a production recommendation.
  • Docker Engine and the Compose plugin (installed in the next step).
  • Ports 8080 and 50051 reachable from wherever the client runs.

1. Install Docker Engine and the Compose plugin

Weaviate ships as a container, so Docker is the only real dependency. Add Docker’s official repository, which is keyed on the Ubuntu codename and resolves correctly on both 26.04 (resolute) and 24.04 (noble):

sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list

Install the engine, CLI, and the Compose plugin, then add your user to the docker group:

sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER

Log out and back in so the group change takes effect, then confirm both binaries respond:

docker --version
docker compose version

On the test server this reported a current engine and Compose plugin:

Docker version 29.6.2, build dfc4efb
Docker Compose version v5.3.1

If you want a deeper walk-through of the plugin and its differences from the old docker-compose binary, the Docker Compose setup guide covers it. Otherwise the two commands above are all Weaviate needs.

2. Deploy Weaviate with Docker Compose

Create a working directory and generate a strong API key. We keep secrets and the pinned version in an .env file so the Compose file itself carries no literals you need to hunt down later:

mkdir -p ~/weaviate && cd ~/weaviate
openssl rand -hex 24

Copy the generated string. Now create the environment file:

sudo vim ~/weaviate/.env

Paste the key you just generated and set an admin identity. Docker Compose reads this file automatically and substitutes the values into the Compose file:

WEAVIATE_VERSION=1.38.6 #https://github.com/weaviate/weaviate/releases
WEAVIATE_API_KEY=paste-your-openssl-generated-key-here
[email protected]

Weaviate recommends pinning an explicit version rather than tracking a floating tag, which is why the version lives in the .env file with a link to the releases page. Next, create the Compose file:

sudo vim ~/weaviate/docker-compose.yml

This definition enables API-key authentication, disables anonymous access, sets the default vectorizer to none (we supply our own vectors), and persists data to a named volume. The ports bind to 127.0.0.1, so nothing is exposed on the public interface yet. The reverse proxy in step 5 is the only front door we open:

services:
  weaviate:
    image: cr.weaviate.io/semitechnologies/weaviate:${WEAVIATE_VERSION}
    container_name: weaviate
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
      - "127.0.0.1:50051:50051"
    volumes:
      - weaviate_data:/var/lib/weaviate
    environment:
      QUERY_DEFAULTS_LIMIT: 25
      PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
      DEFAULT_VECTORIZER_MODULE: 'none'
      CLUSTER_HOSTNAME: 'node1'
      AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'false'
      AUTHENTICATION_APIKEY_ENABLED: 'true'
      AUTHENTICATION_APIKEY_ALLOWED_KEYS: '${WEAVIATE_API_KEY}'
      AUTHENTICATION_APIKEY_USERS: '${WEAVIATE_ADMIN_USER}'
      AUTHORIZATION_ADMINLIST_ENABLED: 'true'
      AUTHORIZATION_ADMINLIST_USERS: '${WEAVIATE_ADMIN_USER}'
volumes:
  weaviate_data:

Pull the image and start the container:

cd ~/weaviate
docker compose up -d

Give it a few seconds, then check the container is up and the readiness probe answers. The readiness endpoint needs no authentication, so it is the cleanest first check:

docker compose ps
curl -s -o /dev/null -w 'ready: HTTP %{http_code}\n' localhost:8080/v1/.well-known/ready

The container shows both ports published and the probe returns 200:

Weaviate vector database container running on Docker Compose with ports 8080 and 50051 on Ubuntu 26.04

Weaviate is now listening. The next step proves that the API key is actually being enforced before we point any client at it.

3. Confirm API-key authentication is working

Pull the key out of the .env file into a shell variable so the commands below stay copy-paste clean:

export WEAVIATE_API_KEY=$(grep '^WEAVIATE_API_KEY=' ~/weaviate/.env | cut -d= -f2)

Call the metadata endpoint with no credentials first. Because anonymous access is disabled, Weaviate rejects it:

curl -s -o /dev/null -w '%{http_code}\n' localhost:8080/v1/meta

You get a hard 401. Now send the same request with the bearer token and Weaviate answers with its version and hostname:

curl -s -H "Authorization: Bearer ${WEAVIATE_API_KEY}" localhost:8080/v1/meta | jq '{version, hostname}'

The 401-then-200 pattern is the quickest confirmation that authentication is on and the key is valid:

Weaviate REST meta endpoint returning HTTP 401 without an API key and version 1.38.6 with the key on Ubuntu

Authentication is only half of it. The admin list decides what an authenticated key may do. The key in AUTHORIZATION_ADMINLIST_USERS gets full read and write. You hand out read-only keys by adding them to AUTHORIZATION_ADMINLIST_READONLY_USERS, and a key that is in neither list is authenticated but authorized for nothing, so its writes come back 403 while reads still work. Start with the single admin key and add scoped keys as you onboard more callers.

With the key proven, the same token is what the Python client will use to connect.

Create a virtual environment and install the Weaviate Python client. Ubuntu marks its system Python as externally managed, so a venv is the correct way to install packages:

sudo apt-get install -y python3-venv python3-pip
python3 -m venv ~/weaviate/venv
~/weaviate/venv/bin/pip install -U weaviate-client

Because we set the vectorizer to none, we provide our own vectors on every object. That keeps the example self-contained: no embedding service to call, just numbers going in and similarity coming out. In a real pipeline you would generate these vectors with an embedding model, for example one served locally by Ollama. Create the script:

sudo vim ~/weaviate/quickstart.py

The script connects with the API key, creates a collection that accepts self-provided vectors, inserts four short documents with three-dimensional toy vectors, then searches for the neighbours closest to a query vector:

import os
import weaviate
from weaviate.classes.config import Configure, DataType, Property
from weaviate.classes.init import Auth
from weaviate.classes.query import MetadataQuery

client = weaviate.connect_to_local(
    host="localhost", port=8080, grpc_port=50051,
    auth_credentials=Auth.api_key(os.environ["WEAVIATE_API_KEY"]),
)
print("client ready:", client.is_ready())

if client.collections.exists("Article"):
    client.collections.delete("Article")

articles = client.collections.create(
    name="Article",
    properties=[
        Property(name="title", data_type=DataType.TEXT),
        Property(name="topic", data_type=DataType.TEXT),
    ],
    vector_config=Configure.Vectors.self_provided(),
)
print("collection created:", articles.name)

docs = [
    ("Install Kubernetes with kubeadm", "kubernetes", [0.90, 0.10, 0.00]),
    ("PostgreSQL streaming replication", "databases", [0.10, 0.90, 0.10]),
    ("Harden SSH on Ubuntu Server",     "security",   [0.00, 0.10, 0.90]),
    ("Deploy a lightweight K3s cluster","kubernetes", [0.85, 0.15, 0.05]),
]
for title, topic, vec in docs:
    articles.data.insert(properties={"title": title, "topic": topic}, vector=vec)
print("inserted:", len(docs))
print("object count:", articles.aggregate.over_all(total_count=True).total_count)

res = articles.query.near_vector(
    near_vector=[0.88, 0.12, 0.02], limit=3,
    return_metadata=MetadataQuery(distance=True),
)
print("--- nearest neighbours to a kubernetes-like vector ---")
for o in res.objects:
    print(f"{o.properties['title']:34s} | {o.properties['topic']:11s} | distance={o.metadata.distance:.4f}")

client.close()

Run it with the same key exported earlier in your shell:

~/weaviate/venv/bin/python ~/weaviate/quickstart.py

The search returns the two Kubernetes documents first, with tiny cosine distances, and pushes the databases entry far down. That ranking is Weaviate doing the actual similarity math over the vectors we supplied:

Weaviate Python v4 client near_vector search returning nearest neighbours with cosine distances

One detail worth remembering from the API: Configure.Vectors.self_provided() is the current way to say “I bring my own vectors” in the v4 client. If you later add a module like text2vec-transformers, you swap that one line for the module configuration and drop the explicit vector= argument on insert, and Weaviate embeds the text for you.

5. Put Weaviate behind Nginx with HTTPS

Weaviate speaks plain HTTP, so never expose port 8080 straight to the internet. Terminate TLS at Nginx and proxy to the local container. Point a DNS A record for your chosen hostname at the server and make sure port 80 is reachable, then pull the domain into a variable:

export WEAVIATE_DOMAIN="weaviate.example.com"

Install Nginx and create a server block. Use a placeholder for the hostname so the file stays generic:

sudo apt-get install -y nginx
sudo vim /etc/nginx/sites-available/weaviate

Add the reverse proxy definition:

server {
    listen 80;
    server_name WEAVIATE_DOMAIN_HERE;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Substitute the real hostname from your variable, enable the site, drop the default, and reload. The placeholder is there so you change one value instead of editing the file by hand:

sudo sed -i "s/WEAVIATE_DOMAIN_HERE/${WEAVIATE_DOMAIN}/" /etc/nginx/sites-available/weaviate
sudo ln -sf /etc/nginx/sites-available/weaviate /etc/nginx/sites-enabled/weaviate
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

With the proxy live, a request carrying the bearer token reaches Weaviate through Nginx and comes back with the version, which confirms the whole path works before you add TLS:

curl -s -H "Authorization: Bearer ${WEAVIATE_API_KEY}" http://${WEAVIATE_DOMAIN}/v1/meta | jq .version

Open the firewall before requesting the certificate so the HTTP-01 challenge on port 80 can complete. Allow SSH first so you do not lock yourself out, allow the web ports, then turn UFW on:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw --force enable

Now issue the certificate. The default path uses the HTTP-01 challenge, which works with any DNS provider as long as port 80 is reachable:

sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d "${WEAVIATE_DOMAIN}" --non-interactive --agree-tos --redirect -m [email protected]

Certbot rewrites the server block to listen on 443, installs the certificate, and redirects HTTP to HTTPS. Because the container ports are bound to 127.0.0.1, Nginx on 443 is now the only public way in.

One caveat about the client. This proxy fronts the REST API. The Python client also uses gRPC on port 50051, which is bound to localhost, so run the client on this host as the guide does, or publish gRPC on the private interface (for example 10.0.0.5:50051:50051 in the Compose file) and restrict it with the firewall. Keeping the client on the same host or private network as Weaviate is the simplest arrangement and avoids proxying gRPC entirely.

If the server is private or behind NAT

When port 80 is not reachable from the internet, use a DNS-01 challenge instead. Install the plugin for your DNS provider (Cloudflare shown here, but Route 53, DigitalOcean, Google Cloud DNS, and others have equivalents) and validate by DNS record rather than an inbound HTTP request:

sudo apt-get install -y python3-certbot-dns-cloudflare
sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d "${WEAVIATE_DOMAIN}" --non-interactive --agree-tos -m [email protected]

Substitute your provider’s plugin package and credentials file if you are not on Cloudflare. Unlike the --nginx path, certonly does not touch your Nginx config, so add the HTTPS server block yourself:

sudo vim /etc/nginx/sites-available/weaviate

Replace the port 80 block with a 443 listener that references the issued certificate and keeps the same proxy settings:

server {
    listen 443 ssl;
    server_name weaviate.example.com;

    ssl_certificate     /etc/letsencrypt/live/weaviate.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/weaviate.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Reload Nginx with sudo systemctl reload nginx and the certificate obtained by DNS is live behind HTTPS.

What Weaviate connects to

The install is the easy part. Weaviate earns its keep once it sits between an embedding source and a retrieval pipeline, so here is what usually plugs into it:

PieceWhat it doesHow it connects
Embedding modelTurns text or images into vectorsA text2vec-* module, or self-provided vectors from a model you run
LangChain / LlamaIndexRAG orchestrationBuilt-in Weaviate vector-store integrations over the Python client
Official clientsApplication accessPython, JavaScript/TypeScript, Go, Java over REST (8080) and gRPC (50051)
Weaviate Cloud ConsoleBrowse collections and run queriesConnects to a reachable instance for a read-only look at your data

If you are still choosing an engine, it is worth putting Weaviate next to the alternatives. The Qdrant setup is a close competitor with a similar single-binary story, and pgvector is the pragmatic choice when you already run PostgreSQL and would rather not add a new service. For an end-to-end retrieval build, the RAG walkthrough with Ollama and LangChain shows the same wiring pattern applied to a full pipeline. Whichever you land on, the shape stays the same: a model produces vectors, the database stores and searches them, and your application talks to it over an authenticated connection.

Keep reading

Claude Code Cheat Sheet – Commands, Shortcuts, Tips AI Claude Code Cheat Sheet – Commands, Shortcuts, Tips Ollama Models Cheat Sheet 2026 (gpt-oss, Qwen3-Coder, DeepSeek) AI Ollama Models Cheat Sheet 2026 (gpt-oss, Qwen3-Coder, DeepSeek) OpenCode CLI Cheat Sheet – Commands and Workflows AI OpenCode CLI Cheat Sheet – Commands and Workflows Best LLM and AI Engineering Books to Read in 2026 AI Best LLM and AI Engineering Books to Read in 2026 Best MLOps Books for 2026 AI Best MLOps Books for 2026 Install Pouch Container Engine on Ubuntu  / CentOS 7 Containers Install Pouch Container Engine on Ubuntu / CentOS 7

Leave a Comment

Press ESC to close