Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Weaviate Ubuntu
GUIDEInstall Guides

How to Install Weaviate on Ubuntu 24.04 VPS: Open-Source Vector Database with Hybrid Search

33 min read

How to Install Weaviate on Ubuntu 24.04 VPS: Open-Source Vector Database with Hybrid Search

Weaviate is one of the most powerful open-source vector databases available today, combining semantic vector search with traditional keyword (BM25) search in a single engine. If you are building Retrieval-Augmented Generation (RAG) pipelines, AI-powered search, recommendation systems, or any application that needs to find information by meaning rather than exact keywords, Weaviate gives you a production-grade foundation you can run entirely on your own VPS. This tutorial walks you through deploying Weaviate on Ubuntu 24.04 using Docker Compose, configuring optional AI modules, designing collections, running hybrid queries, setting up S3 backups, and placing the whole thing behind an Nginx reverse proxy with TLS.

Prefer a different vector database? See our guides for Qdrant and Milvus. If you want to run LLMs locally to pair with Weaviate, follow our Ollama install guide.

Table of Contents

  • What is Weaviate?
  • Why Self-Host Weaviate on Your VPS?
  • Prerequisites
  • Step 1: Update System Packages
  • Step 2: Install Docker and Docker Compose
  • Step 3: Create the Weaviate Docker Compose File
  • Step 4: Configure Environment Variables
  • Step 5: Launch Weaviate
  • Step 6: Create Collections and Load Data
  • Step 7: BYO Vectors vs Module Vectorizer
  • Step 8: Query with GraphQL and REST
  • Step 9: Hybrid Search (BM25 + Vector)
  • Step 10: Filters and Where Clauses
  • Step 11: Back Up Weaviate to S3
  • Step 12: Secure Weaviate with Nginx and TLS
  • Performance Tuning
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is Weaviate?

    Weaviate is an open-source, AI-native vector database written in Go. Unlike traditional databases that index rows by primary key or full-text token, Weaviate stores vector embeddings -- dense numerical representations of text, images, or other data -- and indexes them using Hierarchical Navigable Small World (HNSW) graphs. This lets you ask questions like "find me documents similar in meaning to this query" in milliseconds, even across hundreds of millions of records.

    What sets Weaviate apart from other vector databases is its modular architecture. Out of the box, Weaviate can run as a pure vector store where you supply your own embeddings. But with its built-in module system, it can also vectorize your data automatically using transformer models (text2vec-transformers, text2vec-openai, text2vec-cohere), generate answers from retrieved context (generative-openai, generative-anthropic, generative-ollama), rerank results with cross-encoders (reranker-transformers, reranker-cohere), and even classify or summarize content. This "database + AI" design means you can build complete RAG pipelines without wiring together five separate services.

    Weaviate supports both GraphQL and REST APIs, plus native gRPC for high-throughput clients. Its query language natively handles hybrid search, which combines sparse keyword scoring (BM25) with dense vector similarity and merges them using a configurable alpha weight. That single feature -- hybrid search done well -- is the reason many teams pick Weaviate over simpler vector-only stores. You get the recall of semantic search plus the precision of keyword matching in one query.

    Common Weaviate use cases include semantic document search over wikis and knowledge bases, RAG backends for AI assistants and chatbots, product recommendations based on embedding similarity, image search using CLIP or similar multimodal models, duplicate detection in large catalogs, customer support triage matching new tickets to past resolutions, and e-commerce search that understands "comfy running shoes for wide feet" without requiring those exact keywords to appear in the product description.

    Why Self-Host Weaviate on Your VPS?

    Managed vector database services charge per dimension, per vector, and per query. Costs escalate quickly once you cross a few million records. Running Weaviate on your own VPS gives you several concrete advantages:

    • Flat, predictable pricing -- A single VPS runs your entire Weaviate deployment regardless of vector count. No per-query, per-vector, or per-GB fees.
    • Data sovereignty -- Your embeddings stay on your server. For teams handling proprietary documents, customer data, or regulated content (GDPR, HIPAA), self-hosting eliminates the third-party data-processing agreement problem.
    • No vendor lock-in -- Weaviate is Apache 2.0 licensed. The same Docker image runs on any infrastructure. Switch providers, move to bare metal, or migrate to Kubernetes without rewriting a line of application code.
    • Full module control -- On managed Weaviate Cloud you are limited to the modules the provider enables. Self-hosted, you can plug in any embedding model, any LLM (including local ones via generative-ollama), or custom Python inference containers.
    • Tight coupling with other self-hosted AI -- If you already run Ollama, Llama.cpp, or Hugging Face TGI on the same server, Weaviate can call them over localhost with zero egress cost and sub-millisecond latency.
    • Unlimited experimentation -- Spin up multiple collections, test different vectorizers, reindex on a whim. No cost anxiety.

    Cost Comparison: Weaviate Cloud vs. Self-Hosted

    ScenarioWeaviate Cloud ServerlessPinecone StandardSelf-Hosted Weaviate (VPS)
    Monthly cost (10M vectors, 768 dims)~$295/mo~$350/moEUR 19.99/mo (flat)
    Included storageMeteredMetered100 GB NVMe
    Included queriesMeteredMeteredUnlimited
    Data leaves your infrastructure?YesYesNo
    Custom modulesLimitedNoUnlimited
    Hybrid searchYesLimitedYes (first-class)
    Backup flexibilityVendor formatVendor formatS3, filesystem, GCS
    For any production workload above roughly one million vectors, self-hosting on a CloudCore Professional VPS pays for itself within the first month.

    Prerequisites

    Before you begin, make sure you have:

    • A VPS running Ubuntu 24.04 LTS with root or sudo access
    • SSH access to the server
    • At least 8 GB of RAM (12 GB+ recommended if you plan to enable the text2vec-transformers module, which loads a sentence-transformer model in-process)
    • At least 40 GB of free disk space -- Weaviate stores HNSW graphs and object data on disk; one million 768-dimension vectors takes roughly 3-5 GB with replication overhead
    • A domain name pointed at your server's public IP (required for Nginx + Let's Encrypt TLS)
    Recommended Plan: CloudCore Professional
    >
    For a production Weaviate deployment serving millions of vectors with hybrid search and an on-device vectorizer module, we recommend the CloudCore Professional plan:
    >
    - 6 vCPU cores
    - 12 GB RAM
    - 100 GB NVMe SSD
    - Unmetered bandwidth
    - EUR 19.99/month
    >
    This gives you enough headroom for Weaviate itself (~2 GB baseline), a transformers inference container (~4-6 GB), HNSW in-memory indexes, and OS overhead. For larger deployments (50M+ vectors or very high QPS), scale up to a 32 GB plan or run Weaviate in clustered mode across multiple VPS instances.

    Connect to your server via SSH:

    bash
    ssh root@your-server-ip

    Step 1: Update System Packages

    Start with a fresh package index and apply any pending security updates. This ensures your kernel and libraries are current before you install Docker.

    bash
    sudo apt update && sudo apt upgrade -y

    If a new kernel was installed, reboot and reconnect:

    bash
    sudo reboot

    Install a few baseline utilities that you will use later:

    bash
    sudo apt install -y curl ca-certificates gnupg ufw jq

    Step 2: Install Docker and Docker Compose

    Weaviate's recommended deployment path is Docker Compose. Install the official Docker Engine and Compose plugin from Docker's own apt repository (the version in Ubuntu's default repos is often outdated).

    Add Docker's official GPG key and repository:

    bash
    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 > /dev/null

    sudo apt update

    Install Docker Engine, the CLI, and the Compose plugin:

    bash
    sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

    Verify both are installed:

    bash
    docker --version
    docker compose version

    Expected output:

    text
    Docker version 27.5.0, build a187fa5
    Docker Compose version v2.32.4

    Enable the Docker daemon to start on boot:

    bash
    sudo systemctl enable --now docker

    Step 3: Create the Weaviate Docker Compose File

    Create a directory to hold your Weaviate deployment and its persistent data:

    bash
    sudo mkdir -p /opt/weaviate
    sudo mkdir -p /var/lib/weaviate/data
    cd /opt/weaviate

    Weaviate uses a docker-compose.yml file to wire together the core database container and any optional module containers. Below is a production-ready Compose file that enables three commonly used modules:

    • text2vec-transformers -- Automatic vectorization using a sentence-transformer model (no external API calls)
    • generative-openai -- Hooks Weaviate results directly into OpenAI completions for single-query RAG
    • reranker-transformers -- Cross-encoder reranking for higher-precision result sets
    Create the file:

    bash
    sudo nano /opt/weaviate/docker-compose.yml

    Paste the following:

    yaml
    services:
      weaviate:
        image: cr.weaviate.io/semitechnologies/weaviate:1.28.2
        container_name: weaviate
        restart: unless-stopped
        ports:
          - "8080:8080"
          - "50051:50051"
        volumes:
          - /var/lib/weaviate/data:/var/lib/weaviate
        environment:
          QUERY_DEFAULTS_LIMIT: 25
          AUTHENTICATION_APIKEY_ENABLED: "true"
          AUTHENTICATION_APIKEY_ALLOWED_KEYS: "${WEAVIATE_API_KEY}"
          AUTHENTICATION_APIKEY_USERS: "[email protected]"
          AUTHORIZATION_ADMINLIST_ENABLED: "true"
          AUTHORIZATION_ADMINLIST_USERS: "[email protected]"
          PERSISTENCE_DATA_PATH: "/var/lib/weaviate"
          DEFAULT_VECTORIZER_MODULE: "text2vec-transformers"
          ENABLE_MODULES: "text2vec-transformers,generative-openai,reranker-transformers,backup-s3"
          TRANSFORMERS_INFERENCE_API: "http://t2v-transformers:8080"
          RERANKER_INFERENCE_API: "http://reranker-transformers:8080"
          BACKUP_S3_BUCKET: "${BACKUP_S3_BUCKET}"
          BACKUP_S3_ENDPOINT: "${BACKUP_S3_ENDPOINT}"
          BACKUP_S3_USE_SSL: "true"
          AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID}"
          AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY}"
          CLUSTER_HOSTNAME: "node1"
          LOG_LEVEL: "info"
        depends_on:
          - t2v-transformers
          - reranker-transformers
        healthcheck:
          test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/v1/.well-known/ready"]
          interval: 15s
          timeout: 5s
          retries: 5

    t2v-transformers: image: cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-all-MiniLM-L6-v2 container_name: weaviate-t2v restart: unless-stopped environment: ENABLE_CUDA: "0"

    reranker-transformers: image: cr.weaviate.io/semitechnologies/reranker-transformers:cross-encoder-ms-marco-MiniLM-L-6-v2 container_name: weaviate-reranker restart: unless-stopped environment: ENABLE_CUDA: "0"

    Save and exit (Ctrl+O, Enter, Ctrl+X).

    A few notes on module choices:

    • all-MiniLM-L6-v2 produces 384-dimension vectors. It is fast, small (~80 MB), and a strong default for general-purpose English semantic search. For better multilingual quality, swap to paraphrase-multilingual-MiniLM-L12-v2.
    • cross-encoder-ms-marco-MiniLM-L-6-v2 is the standard BEIR-benchmark reranker. It rescores the top-k results from a first-stage retrieval, pushing the best matches to the top.
    • If you do not need automatic vectorization, remove the t2v-transformers service and change DEFAULT_VECTORIZER_MODULE to none. You can then supply your own embeddings from OpenAI, Cohere, or any other source (see Step 7).

    Step 4: Configure Environment Variables

    Never hardcode API keys in docker-compose.yml. Create a .env file in the same directory:

    bash
    sudo nano /opt/weaviate/.env

    Generate a strong API key first. From another terminal:

    bash
    openssl rand -hex 32

    Paste the output into the .env file along with your S3 credentials:

    bash
    WEAVIATE_API_KEY=paste-the-generated-hex-string-here
    BACKUP_S3_BUCKET=my-weaviate-backups
    BACKUP_S3_ENDPOINT=s3.eu-central-1.amazonaws.com
    AWS_ACCESS_KEY_ID=AKIAXXXXXXXXXXXXXXXX
    AWS_SECRET_ACCESS_KEY=your-secret-key-here

    If you do not plan to configure S3 backups yet, leave the S3 values blank -- the backup-s3 module only activates when you call the backup endpoint.

    Lock down the permissions:

    bash
    sudo chmod 600 /opt/weaviate/.env

    Key environment variables and what they do:

    • PERSISTENCE_DATA_PATH -- Where Weaviate stores HNSW indexes, inverted indexes, and object data. Map this to a host volume so your data survives container rebuilds.
    • AUTHENTICATION_APIKEY_ENABLED -- Turns on API key auth. With this set to true, all clients must send the key in the Authorization: Bearer <key> header.
    • AUTHENTICATION_APIKEY_ALLOWED_KEYS -- A comma-separated list of valid keys. You can issue multiple keys and rotate them by removing old ones.
    • AUTHORIZATION_ADMINLIST_ENABLED / ..._USERS -- Grants admin privileges (schema modification, deletion) to specific user identifiers.
    • DEFAULT_VECTORIZER_MODULE -- Which module to use when a collection does not explicitly override it. Set to none if you always supply your own vectors.
    • ENABLE_MODULES -- Comma-separated list of modules to load at startup.
    • QUERY_DEFAULTS_LIMIT -- Default limit value when a query does not specify one. Prevents accidental full-table scans.

    Step 5: Launch Weaviate

    With the Compose file and environment in place, start the stack:

    bash
    cd /opt/weaviate
    sudo docker compose up -d

    On first launch, Docker will pull three images totalling about 2.5 GB. This takes 1-4 minutes depending on bandwidth.

    Check that all three containers are running:

    bash
    sudo docker compose ps

    Expected output:

    text
    NAME                  IMAGE                                                             STATUS         PORTS
    weaviate              cr.weaviate.io/semitechnologies/weaviate:1.28.2                   Up (healthy)   0.0.0.0:8080->8080/tcp, 0.0.0.0:50051->50051/tcp
    weaviate-reranker     cr.weaviate.io/semitechnologies/reranker-transformers:...         Up
    weaviate-t2v          cr.weaviate.io/semitechnologies/transformers-inference:...        Up

    Tail the logs to verify everything started cleanly:

    bash
    sudo docker compose logs -f weaviate

    Look for the line:

    text
    {"action":"startup","level":"info","msg":"Serving weaviate at http://[::]:8080"}

    Press Ctrl+C to exit the log stream.

    Test the ready endpoint (this one does not require auth):

    bash
    curl http://localhost:8080/v1/.well-known/ready

    Expected output: HTTP 200 with empty body.

    Test an authenticated request:

    bash
    source /opt/weaviate/.env
    curl -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      http://localhost:8080/v1/meta | jq .

    Expected output:

    json
    {
      "hostname": "http://[::]:8080",
      "modules": {
        "backup-s3": { "bucketName": "my-weaviate-backups" },
        "generative-openai": { "documentationHref": "..." },
        "reranker-transformers": { "documentationHref": "..." },
        "text2vec-transformers": { "documentationHref": "..." }
      },
      "version": "1.28.2"
    }

    Your Weaviate instance is up, authenticated, and knows about every module you enabled.

    Step 6: Create Collections and Load Data

    In Weaviate terminology, a collection (formerly "class") is like a table. It defines the property schema, the vectorizer, the distance metric, and the index parameters.

    Create a collection called Article with three properties -- title, content, and category -- using the transformers vectorizer:

    bash
    curl -X POST http://localhost:8080/v1/schema \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "class": "Article",
        "description": "A knowledge-base article",
        "vectorizer": "text2vec-transformers",
        "moduleConfig": {
          "text2vec-transformers": {
            "poolingStrategy": "masked_mean",
            "vectorizeClassName": false
          },
          "generative-openai": {
            "model": "gpt-4o-mini"
          },
          "reranker-transformers": {}
        },
        "vectorIndexType": "hnsw",
        "vectorIndexConfig": {
          "distance": "cosine",
          "ef": 128,
          "efConstruction": 256,
          "maxConnections": 32
        },
        "invertedIndexConfig": {
          "bm25": {
            "b": 0.75,
            "k1": 1.2
          }
        },
        "properties": [
          {
            "name": "title",
            "dataType": ["text"],
            "tokenization": "word",
            "indexFilterable": true,
            "indexSearchable": true
          },
          {
            "name": "content",
            "dataType": ["text"],
            "tokenization": "word",
            "indexFilterable": false,
            "indexSearchable": true
          },
          {
            "name": "category",
            "dataType": ["text"],
            "tokenization": "field",
            "indexFilterable": true,
            "indexSearchable": false
          }
        ]
      }'

    A few schema decisions worth understanding:

    • distance: cosine is the standard choice for sentence-transformer embeddings. Use dot for some older models or l2-squared for image embeddings.
    • ef and efConstruction trade recall for speed. Higher values give better recall but slower ingestion and slightly slower queries. The defaults above are a solid production starting point.
    • BM25 k1 and b control keyword scoring. The defaults match Elasticsearch's defaults.
    • tokenization: field on category treats the whole string as a single token -- perfect for filtering by exact category names.
    Now insert a few objects in a single batch call:

    bash
    curl -X POST http://localhost:8080/v1/batch/objects \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "objects": [
          {
            "class": "Article",
            "properties": {
              "title": "Installing Ollama on Ubuntu",
              "content": "Ollama lets you run large language models locally on your VPS without any GPU required.",
              "category": "ai"
            }
          },
          {
            "class": "Article",
            "properties": {
              "title": "Setting up Nginx as a reverse proxy",
              "content": "Nginx can sit in front of any HTTP backend and terminate TLS using Let Encrypt certificates.",
              "category": "networking"
            }
          },
          {
            "class": "Article",
            "properties": {
              "title": "Choosing the right VPS plan",
              "content": "Pick RAM based on your largest workload, NVMe storage for database servers, and a provider with predictable monthly pricing.",
              "category": "hosting"
            }
          }
        ]
      }'

    Because the collection is configured with text2vec-transformers, Weaviate automatically sends each object to the inference container, receives a 384-dimension vector, and stores it in the HNSW index. No client-side embedding step required.

    Step 7: BYO Vectors vs Module Vectorizer

    You have two options for getting vectors into Weaviate.

    Option A: Module Vectorizer (Automatic)

    This is what you did in Step 6. Weaviate handles embedding generation internally by calling the configured inference container. Pros: simple, one moving part, vectorize at ingest and query time with a consistent model. Cons: tied to the model the container ships with, and inference adds CPU load to the database host.

    Option B: Bring Your Own Vectors (BYO)

    For tighter cost control, or if you want to use a larger/better model (OpenAI text-embedding-3-large, Cohere embed-v3, or a fine-tuned domain-specific model), compute embeddings in your application and hand Weaviate the vector directly.

    First, create a collection with vectorizer: "none":

    bash
    curl -X POST http://localhost:8080/v1/schema \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "class": "Document",
        "vectorizer": "none",
        "vectorIndexConfig": { "distance": "cosine" },
        "properties": [
          { "name": "title", "dataType": ["text"] },
          { "name": "body", "dataType": ["text"] }
        ]
      }'

    Then insert objects with an explicit vector array (here showing a simplified 4-dim vector; in practice you will use 384, 768, 1024, or 1536 dimensions depending on your embedding model):

    bash
    curl -X POST http://localhost:8080/v1/objects \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "class": "Document",
        "properties": {
          "title": "Example doc",
          "body": "The quick brown fox jumps over the lazy dog."
        },
        "vector": [0.12, -0.88, 0.45, 0.03]
      }'

    At query time, compute the query vector in your application and pass it via nearVector:

    bash
    curl -X POST http://localhost:8080/v1/graphql \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "{ Get { Document(nearVector: {vector: [0.11, -0.80, 0.50, 0.04]}, limit: 5) { title body _additional { distance } } } }"
      }'

    Most teams end up with a mix: BYO for main content collections (using a production embedding model) and module vectorizers for admin or internal-only indexes.

    Step 8: Query with GraphQL and REST

    Weaviate offers two query surfaces. GraphQL is the richer, more expressive API and supports all features including hybrid search and generative modules. REST is simpler and good for CRUD operations.

    GraphQL: Semantic search with nearText

    Because the Article collection uses text2vec-transformers, you can query using natural language without computing embeddings yourself:

    bash
    curl -X POST http://localhost:8080/v1/graphql \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "{ Get { Article(nearText: {concepts: [\"how do I run AI models on my own server\"]}, limit: 3) { title content category _additional { distance certainty } } } }"
      }' | jq .

    Expected output:

    json
    {
      "data": {
        "Get": {
          "Article": [
            {
              "title": "Installing Ollama on Ubuntu",
              "content": "Ollama lets you run large language models locally on your VPS without any GPU required.",
              "category": "ai",
              "_additional": { "distance": 0.21, "certainty": 0.895 }
            },
            {
              "title": "Choosing the right VPS plan",
              "content": "...",
              "category": "hosting",
              "_additional": { "distance": 0.48, "certainty": 0.76 }
            }
          ]
        }
      }
    }

    Notice that the semantic search surfaced the Ollama article for a query that contained none of its keywords.

    REST: Fetch by ID

    bash
    curl -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      "http://localhost:8080/v1/objects/Article/UUID-HERE"

    REST: List all objects in a collection

    bash
    curl -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      "http://localhost:8080/v1/objects?class=Article&limit=10"

    GraphQL: Generative RAG in one request

    With the generative-openai module you can retrieve and generate in a single round trip. Supply your OpenAI key via an extra header:

    bash
    curl -X POST http://localhost:8080/v1/graphql \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "X-OpenAI-Api-Key: sk-..." \
      -H "Content-Type: application/json" \
      -d '{
        "query": "{ Get { Article(nearText: {concepts: [\"running LLMs locally\"]}, limit: 3) { title _additional { generate(singleResult: {prompt: \"Summarize this article in one sentence: {title} -- {content}\"}) { singleResult } } } } }"
      }'

    Weaviate performs the vector search, feeds each result into your prompt template, calls OpenAI, and returns the generated text alongside the source documents. This is the shortest possible path to a working RAG system.

    Step 9: Hybrid Search (BM25 + Vector)

    Semantic vector search is excellent at understanding meaning but can miss exact matches (product codes, part numbers, proper nouns). BM25 keyword search is excellent at precision but misses paraphrases. Hybrid search combines both in a single query and merges the rankings.

    Weaviate's hybrid query runs both searches, normalizes the scores, and combines them using an alpha weight where alpha: 1.0 is pure vector, alpha: 0.0 is pure BM25, and alpha: 0.5 is an equal mix.

    bash
    curl -X POST http://localhost:8080/v1/graphql \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "{ Get { Article(hybrid: {query: \"Nginx TLS setup\", alpha: 0.5}, limit: 5) { title content category _additional { score explainScore } } } }"
      }' | jq .

    The _additional { score explainScore } fields let you inspect how each result ranked and why. A typical response shows the Nginx article at the top (strong BM25 match on "Nginx" plus strong vector similarity on "TLS") followed by progressively less-relevant matches.

    Tuning alpha by use case

    • alpha: 0.75 -- Favor semantic matching. Use for Q&A systems and knowledge-base search where users phrase queries naturally.
    • alpha: 0.5 -- Balanced. Good default for general-purpose search.
    • alpha: 0.25 -- Favor keyword matching. Use for product catalogs, code search, or any domain with lots of specific terminology.
    • alpha: 0.0 -- Pure BM25. Falls back to behavior similar to Elasticsearch.

    Adding a reranker for even higher precision

    Chain the reranker module on top of the hybrid results to get the best of three worlds:

    bash
    curl -X POST http://localhost:8080/v1/graphql \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "{ Get { Article(hybrid: {query: \"how to run LLMs on a VPS without GPU\", alpha: 0.6}, limit: 20) { title content _additional { rerank(property: \"content\", query: \"how to run LLMs on a VPS without GPU\") { score } } } } }"
      }'

    The reranker takes the top 20 hybrid results, rescores them with a cross-encoder (which reads the query and document together, unlike the bi-encoder used for first-stage retrieval), and returns them in the new order. This pattern -- cheap first-stage retrieval of 20-50 candidates followed by expensive reranking of that smaller set -- is the gold standard for RAG pipelines.

    Step 10: Filters and Where Clauses

    Hybrid search is powerful, but real applications need metadata filtering: "find articles similar to X, but only in the ai category and published after 2024". Weaviate's where clause handles this.

    Exact match filter

    bash
    curl -X POST http://localhost:8080/v1/graphql \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "{ Get { Article(nearText: {concepts: [\"reverse proxy\"]}, where: {path: [\"category\"], operator: Equal, valueText: \"networking\"}, limit: 5) { title category } } }"
      }'

    Compound filter with AND

    bash
    curl -X POST http://localhost:8080/v1/graphql \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "{ Get { Article(hybrid: {query: \"ollama\", alpha: 0.5}, where: {operator: And, operands: [{path: [\"category\"], operator: Equal, valueText: \"ai\"}, {path: [\"title\"], operator: Like, valueText: \"Ubuntu\"}]}, limit: 10) { title category } } }"
      }'

    Supported operators

    • Equal, NotEqual, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual
    • Like with * wildcards for text
    • ContainsAny, ContainsAll for array fields
    • IsNull for missing values
    • WithinGeoRange for geospatial coordinates
    Filters are applied before vector search, using Weaviate's inverted index. This keeps hybrid queries fast even on tightly filtered subsets.

    Step 11: Back Up Weaviate to S3

    Data loss is not a theoretical risk. Weaviate ships with a first-class backup system that snapshots HNSW indexes and objects atomically to S3, GCS, Azure Blob, or local filesystem. You already enabled backup-s3 in Step 3.

    Create an S3 bucket

    Using the AWS CLI (or your provider's console), create a bucket in the region you set in BACKUP_S3_ENDPOINT:

    bash
    aws s3 mb s3://my-weaviate-backups --region eu-central-1

    Enable versioning so accidental deletions are recoverable:

    bash
    aws s3api put-bucket-versioning \
      --bucket my-weaviate-backups \
      --versioning-configuration Status=Enabled

    Trigger a backup

    bash
    curl -X POST http://localhost:8080/v1/backups/s3 \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "id": "backup-2026-04-16-daily",
        "include": ["Article", "Document"]
      }'

    Check status:

    bash
    curl -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      http://localhost:8080/v1/backups/s3/backup-2026-04-16-daily | jq .

    Expected output when complete:

    json
    {
      "id": "backup-2026-04-16-daily",
      "path": "s3://my-weaviate-backups/backup-2026-04-16-daily",
      "status": "SUCCESS"
    }

    Restore from a backup

    bash
    curl -X POST http://localhost:8080/v1/backups/s3/backup-2026-04-16-daily/restore \
      -H "Authorization: Bearer $WEAVIATE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"include": ["Article"]}'

    Automate daily backups

    Create a cron entry that hits the backup endpoint every night:

    bash
    sudo crontab -e

    Add:

    text
    0 3   * curl -s -X POST http://localhost:8080/v1/backups/s3 -H "Authorization: Bearer $(grep WEAVIATE_API_KEY /opt/weaviate/.env | cut -d= -f2)" -H "Content-Type: application/json" -d "{\"id\": \"backup-$(date +\%Y\%m\%d)\"}" >> /var/log/weaviate-backup.log 2>&1

    Pair this with an S3 lifecycle policy that expires backups older than 30 days to keep storage costs bounded.

    Step 12: Secure Weaviate with Nginx and TLS

    Weaviate's port 8080 is currently bound to all interfaces. Put Nginx in front of it with Let's Encrypt TLS so clients connect over HTTPS.

    First, restrict Weaviate's port so it is only reachable via Nginx:

    bash
    sudo ufw allow OpenSSH
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw deny 8080/tcp
    sudo ufw deny 50051/tcp
    sudo ufw enable

    Install Nginx and Certbot:

    bash
    sudo apt install -y nginx certbot python3-certbot-nginx

    Create the server block:

    bash
    sudo nano /etc/nginx/sites-available/weaviate

    Paste:

    nginx
    server {
        listen 80;
        server_name weaviate.yourdomain.com;
        return 301 https://$host$request_uri;
    }

    server { listen 443 ssl http2; server_name weaviate.yourdomain.com;

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

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always;

    client_max_body_size 50m;

    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;

    proxy_buffering off; proxy_read_timeout 300s; proxy_send_timeout 300s; }

    location = /v1/.well-known/live { proxy_pass http://127.0.0.1:8080; access_log off; } }

    Enable and reload:

    bash
    sudo ln -s /etc/nginx/sites-available/weaviate /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo certbot --nginx -d weaviate.yourdomain.com
    sudo systemctl reload nginx

    Certbot sets up auto-renewal via systemd timer. Test with:

    bash
    sudo certbot renew --dry-run

    Now every request hits https://weaviate.yourdomain.com and still requires the Bearer token. For gRPC on port 50051, either tunnel through an SSH bastion or use Nginx with stream blocks -- but most HTTP-based clients (Python, JS, Go) work perfectly over the REST/GraphQL path.

    Performance Tuning

    HNSW parameters

    The most impactful tuning levers live in the collection's vectorIndexConfig:

    • ef (query time) -- Higher values search more nodes per query, improving recall but increasing latency. Typical range: 64-512. Start at 128.
    • efConstruction (build time) -- Higher values build a denser graph with better recall but slower ingestion. Typical range: 128-512. Start at 256.
    • maxConnections -- Number of outgoing links per node. Higher improves recall at the cost of memory. Typical range: 16-64. Start at 32.
    You can update ef on a live collection without reindexing; efConstruction and maxConnections only apply to new vectors.

    Memory sizing

    Weaviate keeps the HNSW graph in memory for query speed. Rough rule: plan for vectors dimensions 4 bytes * 1.5 RAM. For 10 million 768-dimension vectors that is roughly 46 GB. Use product quantization (PQ) to compress vectors in memory at a small recall cost:

    json
    {
      "vectorIndexConfig": {
        "pq": { "enabled": true, "segments": 96, "trainingLimit": 100000 }
      }
    }

    PQ typically reduces memory by 8-16x with under 2% recall loss.

    Shard count

    For single-node deployments, one shard is fine. When scaling beyond ~50M vectors, switch to multi-node and configure shards per collection to spread load across nodes.

    Batch ingestion

    Ingesting with the /v1/batch/objects endpoint is 10-50x faster than one-at-a-time /v1/objects calls. Use batches of 100-500 objects per request.

    Troubleshooting

    ProblemCauseSolution
    401 Unauthorized on every requestMissing or wrong Bearer tokenVerify Authorization: Bearer $WEAVIATE_API_KEY header. Check the key in /opt/weaviate/.env matches the one Docker loaded: sudo docker exec weaviate env \</td><td>grep AUTHENTICATION.
    context deadline exceeded on ingestTransformers container still loading modelTail logs: sudo docker compose logs t2v-transformers. Wait until you see Application startup complete. First-time startup takes 30-90s.
    Collection creation fails with module not enabledModule name typo in ENABLE_MODULESCheck spelling against Weaviate docs. Restart stack after editing: sudo docker compose up -d.
    Queries return zero results despite data presentCollection vectorizer mismatchInspect the class config: curl -H "Authorization: Bearer $KEY" http://localhost:8080/v1/schema/Article. Ensure vectorizer matches how data was ingested.
    Out-of-memory killsHNSW graph exceeds RAMEnable PQ compression, reduce maxConnections, or move to a larger plan. Swap file helps as a stopgap: sudo fallocate -l 8G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile.
    Backup fails with NoSuchBucketS3 bucket missing or wrong regionCreate the bucket and verify BACKUP_S3_ENDPOINT matches the region. Keys need s3:PutObject, s3:GetObject, s3:ListBucket.
    docker compose command not foundOld Docker Compose v1 installedInstall Compose v2 plugin: sudo apt install docker-compose-plugin.
    Slow BM25 scoring on large text fieldsInverted index not builtVerify indexSearchable: true on the text property. Reindexing requires recreating the collection.
    Stream logs for real-time debugging:

    bash
    sudo docker compose -f /opt/weaviate/docker-compose.yml logs -f --tail=100

    FAQ

    How does Weaviate compare to Qdrant and Milvus?

    Weaviate is the most feature-rich of the three, with first-class modules for vectorization, generative AI, and reranking. It shines when you want a single system that handles the full RAG pipeline. Its hybrid search implementation is among the best available.

    Qdrant is faster for pure vector search and has a simpler operational footprint (no module containers). It is an excellent choice when you compute embeddings in your application and just need a lightning-fast index. See our Qdrant install guide.

    Milvus is the heavyweight enterprise option, designed for multi-billion-vector deployments with clustered storage backends (MinIO, etcd, Pulsar). Higher operational complexity but scales further than either Weaviate or Qdrant. See our Milvus install guide.

    Pick Weaviate when you want hybrid search, modular AI integrations, and a developer-friendly GraphQL API.

    Does Weaviate work without a GPU?

    Yes. All examples in this guide run entirely on CPU. The text2vec-transformers and reranker-transformers containers use CPU inference by default (ENABLE_CUDA: "0"). A 6 vCPU VPS comfortably ingests a few hundred documents per second with the all-MiniLM-L6-v2 model. For high-throughput production workloads (>1000 docs/sec ingest) or larger embedding models (sentence-transformers v2 XL), a GPU plan helps.

    Can I run Weaviate clustered across multiple VPS?

    Yes. Weaviate supports native clustering via Raft for schema replication and gossip for node discovery. You set CLUSTER_HOSTNAME and CLUSTER_JOIN environment variables on each node. Most single-tenant deployments (even large ones) run single-node until they hit roughly 50M vectors or need fault tolerance. When you cross that threshold, plan a weekend to migrate to a 3-node cluster with shard replication factor 2 or 3.

    How do I migrate data from Pinecone or Weaviate Cloud to self-hosted?

    Pinecone exposes a fetch API that returns vectors and metadata; write a Python script that pages through your namespace and batches into Weaviate's /v1/batch/objects endpoint. From Weaviate Cloud, use the built-in backup module to export to an S3 bucket, then restore from that same bucket into your self-hosted node -- the data format is identical. Typical migration for 10M vectors takes 1-4 hours on a CloudCore Professional VPS.

    What is the right way to use hybrid search alpha?

    Start at alpha: 0.5 and measure. Build a small evaluation set of query/expected-result pairs and sweep alpha from 0.0 to 1.0 in 0.1 steps, recording MRR (mean reciprocal rank) or nDCG at each value. Pick the alpha that maximizes your chosen metric. For most knowledge-base and documentation use cases, the best alpha lands between 0.55 and 0.75. For code search and product catalogs with specific identifiers, it is usually 0.25 to 0.45.

    Can Weaviate handle image or multimodal vectors?

    Yes. Use the multi2vec-clip module to embed images and text into the same 512-dim CLIP space, then query with nearImage or nearText against the same collection. Alternatively, compute CLIP embeddings in your application and use BYO vectors with vectorizer: "none". Multimodal search is a great fit for e-commerce, media libraries, and content moderation pipelines.

    Next Steps

    Now that Weaviate is running on your VPS, here are practical next steps.

    • Plug in a local LLM for end-to-end private RAG -- Combine Weaviate with Ollama and the generative-ollama module to build a RAG pipeline where no data leaves your server. Ask questions of your knowledge base and get answers from a local Llama 3.1 or Mistral model.
    • Compare alternative vector stores -- For pure speed on small-to-medium datasets, benchmark against Qdrant. For massive scale (100M+ vectors), evaluate Milvus.
    • Add a query layer -- Install the Weaviate Python client (pip install -U weaviate-client) or the TypeScript client and start building your application against a typed SDK instead of raw curl.
    • Set up monitoring -- Weaviate exposes Prometheus metrics at /v1/metrics. Scrape them with your monitoring stack and alert on weaviate_objects_durations_ms and weaviate_queries_durations_ms percentiles.
    • Read the official docs -- The Weaviate documentation covers advanced topics like multi-tenancy, cross-references between collections, named vectors, and replication.

    Ready to deploy?
    >
    Weaviate runs beautifully on a CloudCore Professional VPS -- 6 vCPU, 12 GB RAM, 100 GB NVMe, unmetered bandwidth, EUR 19.99/month. Enough headroom for tens of millions of vectors, the transformers module, and your application stack on a single server.
    >
    Deploy your Weaviate VPS now.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket