Enterprise Search
    AI Answers

    Deploy Onyx on a VPS

    Self-host Onyx (formerly Danswer), an open-source enterprise search and AI answer engine, on a RamNode VPS with Docker Compose, Vespa, Postgres, and nginx TLS.

    Onyx (formerly Danswer) is an open-source enterprise search / AI answer engine that connects to your data sources (Slack, Confluence, Google Drive, etc.) and provides a chat/search interface over them. Unlike faster-whisper or Piper, it's a multi-container application (web frontend, API server, background workers, Postgres, Vespa search engine, Redis, MinIO/S3-compatible storage, nginx), so it's deployed via Docker Compose rather than a single service.

    1. Choose and size the VPS

    Onyx is heavy to run — Vespa (its search backend) alone wants real memory.

    Use caseRecommended RamNode plan specs
    Small team / evaluation4 vCPU, 16 GB RAM, 100 GB disk
    Production, moderate document volume8 vCPU, 32 GB RAM, 200+ GB SSD

    Recommended OS: Ubuntu 24.04 LTS. Use a plan with SSD/NVMe storage — Vespa indexing is disk-I/O sensitive.

    2. Initial server setup

    shell
    apt update && apt -y upgrade
    apt -y install ca-certificates curl gnupg ufw git
    
    ufw allow OpenSSH
    ufw allow 80/tcp
    ufw allow 443/tcp
    ufw enable

    3. Install Docker Engine and Compose plugin

    shell
    install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    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" | \
      tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    apt update
    apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    
    systemctl enable --now docker
    docker --version
    docker compose version

    Optional: add a non-root deploy user to the docker group so you don't have to sudo every command:

    shell
    adduser onyx
    usermod -aG docker onyx

    4. Pull down the Onyx repo

    shell
    su - onyx -s /bin/bash
    git clone https://github.com/onyx-dot-app/onyx.git
    cd onyx/deployment/docker_compose

    The deployment/docker_compose directory contains the compose files Onyx maintains for self-hosting (docker-compose.dev.yml for a quick eval setup, and a prod-oriented compose file with nginx included — check the repo's current README, since filenames occasionally change between releases).

    5. Configure environment variables

    Copy the example env file and edit it:

    shell
    cp env.prod.template .env
    nano .env

    At minimum, set:

    shell
    # LLM provider — Onyx needs at least one configured to answer questions
    GEN_AI_API_KEY=your_openai_or_other_provider_key
    GEN_AI_MODEL_PROVIDER=openai
    
    # Auth — set a real secret, don't leave the default
    SECRET=$(openssl rand -hex 32)
    
    # Domain Onyx will be served from
    WEB_DOMAIN=https://onyx.example.com
    
    # Postgres credentials (used by the postgres container in the same compose file)
    POSTGRES_USER=onyx
    POSTGRES_PASSWORD=$(openssl rand -hex 16)

    Onyx supports self-hosted/local LLMs too (via Ollama or a compatible OpenAI-style endpoint) if you don't want to send data to a third-party API — set GEN_AI_MODEL_PROVIDER and the corresponding host/key variables accordingly. Check the current env.prod.template in the repo for the full, up-to-date variable list, since Onyx adds new connectors/settings frequently.

    6. Bring the stack up

    shell
    docker compose -f docker-compose.prod.yml up -d

    This starts, at minimum: web_server, api_server, background, relational_db (Postgres), index (Vespa), cache (Redis), minio, and nginx. First startup is slow — Vespa needs to initialize its index and the model server needs to pull embedding models.

    Watch it come up:

    shell
    docker compose -f docker-compose.prod.yml logs -f api_server

    Check container health:

    shell
    docker compose -f docker-compose.prod.yml ps

    7. TLS termination

    The prod compose file ships its own nginx container listening on 80/443. The cleanest way to get a certificate without fighting two nginxes is to run certbot in standalone mode before/alongside it, or use the setup script Onyx provides:

    shell
    cd onyx/deployment/data/nginx
    ./init-letsencrypt.sh

    Check the script for the domain/email variables it expects — set them at the top of the script or via env vars as documented in the repo before running it. If you'd rather front Onyx with your own host-level nginx and manage certs the way you already do elsewhere, bind Onyx's own nginx container to a local port only (e.g. 127.0.0.1:8080:80 in the compose file) and reverse-proxy from your host nginx into that instead — this fits better if you're centralizing TLS across multiple services on the same VPS.

    8. Enable stack-on-boot

    Docker Compose doesn't auto-restart on VPS reboot unless containers have a restart policy. Confirm (or add) restart: unless-stopped on each service in the compose file — Onyx's shipped prod compose file already sets this on most services, but it's worth checking after you pull a new version, since restart policies do occasionally change between releases.

    Docker itself is already enabled via systemctl enable docker from step 3, so containers with restart: unless-stopped will come back up automatically after a host reboot.

    9. Operational notes

    • Backups: back up the Postgres volume (metadata, users, connector configs) and the Vespa data volume (search index — rebuildable but slow to reindex) and the MinIO volume (uploaded files). docker compose exec relational_db pg_dump -U onyx onyx > backup.sql is a reasonable starting point for Postgres; snapshot the Docker volumes for Vespa/MinIO or use RamNode's VPS-level snapshot/backup feature for the whole disk.
    • Updates: git pull, review the changelog for breaking .env/compose changes, then docker compose -f docker-compose.prod.yml pull && docker compose -f docker-compose.prod.yml up -d.
    • Resource pressure: if Vespa or the embedding model server gets OOM-killed, that's the signal to move up a plan tier rather than tune around it — these components are memory-hungry by design.
    • Monitoring: add HTTP checks against the web server's root path and the API server's health endpoint to your monitoring stack; also watch docker compose ps for restart loops, which usually means an env var was missed.
    • Connectors: once the stack is up, most configuration (data source connectors, LLM provider, user management) happens through the Onyx admin UI rather than further file edits.