Databases

    Deploy ChromaDB on a VPS

    An open-source vector database for AI applications — semantic search, RAG pipelines, and recommendation systems on your own infrastructure.

    Introduction

    ChromaDB is an open-source vector database designed for AI applications. It stores and retrieves high-dimensional vector embeddings alongside metadata, making it a solid backend for retrieval-augmented generation (RAG) pipelines, semantic search, and recommendation systems. Unlike heavyweight alternatives, ChromaDB is lightweight enough to run comfortably on a budget VPS while handling production workloads for small to medium-scale applications.

    This guide walks through deploying ChromaDB on a RamNode VPS using Docker, configuring persistent storage, enabling token authentication, securing the port with a reverse proxy, and setting up automatic restarts.

    Server Requirements

    ComponentRecommended
    RAM2 GB minimum (1 GB workable for small collections)
    CPU1 vCPU minimum; 2+ vCPUs for concurrent embedding requests
    Disk10 GB+ SSD; scale based on collection sizes
    OSUbuntu 22.04 LTS or Debian 12
    RamNode PlanKVM SSD 2 ($6/mo) or higher

    Prerequisites:

    • • Docker and Docker Compose installed and running
    • • A non-root user with sudo privileges
    • • Ports 8000 (ChromaDB) and 443 (HTTPS) open in your firewall
    • • A domain name pointed at your VPS IP (for the HTTPS/Nginx section)
    1

    Prepare the Server

    Update packages and install Docker:

    System update & Docker install
    sudo apt update && sudo apt upgrade -y
    
    # Install Docker
    curl -fsSL https://get.docker.com | sh
    sudo usermod -aG docker $USER
    newgrp docker
    
    # Verify Docker is running
    docker --version
    docker compose version
    2

    Create the Project Directory

    Create directories
    mkdir -p ~/chromadb/data
    cd ~/chromadb

    The data directory stores ChromaDB's persistent SQLite and vector index files. Keeping this separate from the config makes backups and upgrades straightforward.

    3

    Generate an Authentication Token

    ChromaDB supports static token authentication. Generate a secure random token:

    Generate token
    openssl rand -hex 32

    Save this token — you'll pass it as CHROMA_SERVER_AUTHN_CREDENTIALS and use it as a Bearer token in all client requests.

    4

    Write the Docker Compose File

    Create compose file
    nano ~/chromadb/docker-compose.yml
    docker-compose.yml
    version: '3.9'
    
    services:
      chromadb:
        image: chromadb/chroma:latest
        container_name: chromadb
        restart: unless-stopped
        ports:
          - "127.0.0.1:8000:8000"
        volumes:
          - ./data:/chroma/chroma
        environment:
          - CHROMA_SERVER_AUTHN_PROVIDER=chromadb.auth.token_authn.TokenAuthenticationServerProvider
          - CHROMA_SERVER_AUTHN_CREDENTIALS=YOUR_TOKEN_HERE
          - CHROMA_SERVER_CORS_ALLOW_ORIGINS=["*"]
          - IS_PERSISTENT=TRUE
          - PERSIST_DIRECTORY=/chroma/chroma
        healthcheck:
          test: ["CMD", "curl", "-f", "http://localhost:8000/api/v1/heartbeat"]
          interval: 30s
          timeout: 10s
          retries: 3

    Important: The port binding 127.0.0.1:8000:8000 exposes ChromaDB only on localhost. External access goes through Nginx with TLS. Never bind directly to 0.0.0.0 on a production server.

    5

    Start ChromaDB

    Start and verify
    cd ~/chromadb
    docker compose up -d
    
    # Check container status
    docker compose ps
    
    # Watch logs for startup errors
    docker compose logs -f chromadb

    Test the heartbeat endpoint locally:

    Heartbeat test
    curl http://localhost:8000/api/v1/heartbeat
    # Expected: {"nanosecond heartbeat": <timestamp>}
    6

    Configure Nginx as a Reverse Proxy

    Install Nginx and Certbot
    sudo apt install -y nginx certbot python3-certbot-nginx
    /etc/nginx/sites-available/chromadb
    server {
        listen 80;
        server_name your-domain.com;
    
        location / {
            proxy_pass http://127.0.0.1:8000;
            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;
    
            # Support large embedding payloads
            client_max_body_size 50M;
            proxy_read_timeout 120s;
        }
    }
    Enable site and get TLS
    sudo ln -s /etc/nginx/sites-available/chromadb /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx
    
    # Issue TLS certificate
    sudo certbot --nginx -d your-domain.com
    7

    Configure the Firewall

    UFW rules
    sudo ufw allow OpenSSH
    sudo ufw allow 'Nginx Full'
    sudo ufw enable
    
    # Confirm ChromaDB port is NOT exposed directly
    sudo ufw status
    # Port 8000 should NOT appear in the list
    8

    Connect a Client

    Test the full HTTPS path with your auth token:

    cURL test
    # Test heartbeat over HTTPS
    curl -H "Authorization: Bearer YOUR_TOKEN_HERE" \
      https://your-domain.com/api/v1/heartbeat
    
    # List collections
    curl -H "Authorization: Bearer YOUR_TOKEN_HERE" \
      https://your-domain.com/api/v1/collections

    Python client example:

    Install client
    pip install chromadb
    Python usage
    import chromadb
    from chromadb.config import Settings
    
    client = chromadb.HttpClient(
        host="your-domain.com",
        port=443,
        ssl=True,
        headers={"Authorization": "Bearer YOUR_TOKEN_HERE"}
    )
    
    # Create a collection
    collection = client.create_collection("my-docs")
    
    # Add documents with embeddings
    collection.add(
        documents=["RamNode is a VPS provider", "ChromaDB is a vector database"],
        ids=["doc1", "doc2"]
    )
    
    # Query by semantic similarity
    results = collection.query(
        query_texts=["cloud hosting"],
        n_results=2
    )
    print(results)

    Backups

    ChromaDB stores all data as files under the mounted volume. Stop the container, archive the data directory, and restart:

    Backup script
    cd ~/chromadb
    
    # Stop the container cleanly before backup
    docker compose stop chromadb
    
    # Create a timestamped archive
    tar -czf ~/backups/chromadb-$(date +%Y%m%d-%H%M%S).tar.gz ./data
    
    # Restart
    docker compose start chromadb

    For automated backups, add this as a cron job or pair it with RamNode block storage for off-VPS redundancy.

    Upgrading ChromaDB

    Upgrade
    cd ~/chromadb
    docker compose pull
    docker compose up -d
    
    # Verify the new version is running
    docker compose exec chromadb chroma --version

    Always check the ChromaDB release notes before upgrading — major versions occasionally include migration steps for the on-disk format.

    Troubleshooting

    Container exits immediately on startup

    Check logs for permission errors: docker compose logs chromadb. Fix with sudo chown -R 1000:1000 ~/chromadb/data

    401 Unauthorized errors

    Confirm you're passing the Authorization: Bearer <token> header. The token must match CHROMA_SERVER_AUTHN_CREDENTIALS exactly, with no trailing whitespace.

    Connection refused on port 8000

    Port 8000 is bound to localhost only by design. All external traffic must go through Nginx on port 443. Test locally with localhost:8000.

    Nginx returns 502 Bad Gateway

    ChromaDB container is likely not running or has crashed. Check: docker compose ps and docker compose restart chromadb

    Summary

    You now have a production-ready ChromaDB instance with:

    • Persistent storage — data survives container restarts and upgrades
    • Token authentication — all API requests require a Bearer token
    • TLS via Nginx — traffic encrypted with a free Let's Encrypt certificate
    • Localhost binding — ChromaDB port not exposed to the internet
    • Auto-restart — the unless-stopped policy keeps the service up

    ChromaDB integrates directly with LangChain, LlamaIndex, and the OpenAI embeddings API, making this a solid foundation for RAG applications and AI-powered search.