Search Engine
    HA Cluster

    Deploy Typesense on a VPS

    Self-host Typesense search on a RamNode VPS — single-node Docker or native install, 3-node Raft HA cluster, nginx TLS, and API key management.

    Target environment: RamNode KVM VPS, Ubuntu 24.04 LTS. Covers a single-node quick deploy and a 3-node HA cluster, both fronted by nginx + Let's Encrypt.


    1. Sizing and prerequisites

    • Typesense keeps its index in RAM plus a disk-backed persistence layer (RocksDB). Budget RAM at roughly 2–3x your raw dataset size as a starting point.
    • Single-node: any RamNode VPS with enough RAM for your dataset. Attach a dedicated data volume if your plan supports it, otherwise use a subdirectory on the root disk with adequate free space.
    • HA cluster: 3 RamNode VPS nodes (odd number required — Typesense uses Raft consensus, needs a quorum). Put them in the same datacenter region for low-latency Raft replication; cross-region Raft is possible but latency will show up as write slowness.
    • Firewall: Typesense API port (default 8108) should NOT be exposed publicly — put it behind nginx, or restrict to your app servers' IPs at the RamNode firewall/security-group level. The Raft peering port (default 8107) should be restricted to just the cluster nodes.

    2. Single-node install (Docker)

    shell
    sudo mkdir -p /opt/typesense/data
    sudo apt-get update && sudo apt-get install -y docker.io
    sudo systemctl enable --now docker
    
    # Generate an API key up front
    TYPESENSE_API_KEY=$(openssl rand -hex 32)
    echo "Save this key: $TYPESENSE_API_KEY"
    
    sudo docker run -d \
      --name typesense \
      --restart unless-stopped \
      -p 127.0.0.1:8108:8108 \
      -v /opt/typesense/data:/data \
      typesense/typesense:27.1 \
      --data-dir /data \
      --api-key="$TYPESENSE_API_KEY" \
      --enable-cors

    Note the bind is to 127.0.0.1 only — nginx on the same box proxies to it; the container port is not reachable from outside the VPS directly.

    Verify:

    shell
    curl "http://localhost:8108/health"
    curl -H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" "http://localhost:8108/collections"

    3. Single-node install (native binary, no Docker)

    If you'd rather run it as a systemd unit directly (running it as a plain systemd service):

    shell
    curl -O https://dl.typesense.org/releases/27.1/typesense-server-27.1-amd64.deb
    sudo apt install ./typesense-server-27.1-amd64.deb

    Edit /etc/typesense/typesense-server.ini:

    shell
    [server]
    api-address = 127.0.0.1
    api-port = 8108
    data-dir = /var/lib/typesense
    api-key = <your-generated-key>
    enable-cors = true
    shell
    sudo systemctl enable --now typesense-server
    sudo systemctl status typesense-server

    4. nginx reverse proxy + TLS

    Standard Let's Encrypt setup with certbot:

    shell
    server {
        listen 443 ssl;
        server_name search.internal.ramnode.example;
    
        ssl_certificate     /etc/letsencrypt/live/search.internal.ramnode.example/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/search.internal.ramnode.example/privkey.pem;
    
        location / {
            proxy_pass http://127.0.0.1:8108;
            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;
        }
    }
    shell
    sudo certbot --nginx -d search.internal.ramnode.example

    Since the API key is the only auth layer, also restrict access at the firewall to known app-server IPs, and consider a second nginx location block using scoped/search-only API keys (see section 6) for anything client-facing.

    5. HA cluster (3 nodes, Raft)

    On each node, systemd/native install as in section 3, but with cluster-aware config. Example for node 1 of 3 (10.0.0.11, 10.0.0.12, 10.0.0.13 as private IPs):

    shell
    [server]
    api-address = 0.0.0.0
    api-port = 8108
    peering-address = 10.0.0.11
    peering-port = 8107
    data-dir = /var/lib/typesense
    api-key = <shared-key-same-on-all-nodes>
    nodes = 10.0.0.11:8107:8108,10.0.0.12:8107:8108,10.0.0.13:8107:8108

    Write nodes to a shared file each node reads at startup (Typesense also supports --nodes pointing at a file path — useful if you manage this via Ansible and want to template it once):

    shell
    echo "10.0.0.11:8107:8108,10.0.0.12:8107:8108,10.0.0.13:8107:8108" | sudo tee /etc/typesense/nodes

    Then reference nodes = /etc/typesense/nodes in the ini instead of the inline list.

    Restart all three, then check cluster health from any node:

    shell
    curl -H "X-TYPESENSE-API-KEY: <key>" http://localhost:8108/health
    curl -H "X-TYPESENSE-API-KEY: <key>" http://localhost:8108/debug

    /debug on the leader shows Raft state and committed index; use this to confirm all three nodes agree before pointing production traffic at the cluster. Put all three behind a single nginx upstream with least_conn or round-robin — reads can go to any node, writes are automatically forwarded to the leader internally.

    RamNode firewall notes for the cluster: allow 8107/tcp and 8108/tcp between the three private IPs only; do not open peering port 8107 beyond the cluster nodes.

    6. Scoped/search-only API keys

    Don't hand out the admin key to anything client-facing. Generate scoped keys per collection:

    shell
    curl -X POST "http://localhost:8108/keys" \
      -H "X-TYPESENSE-API-KEY: <admin-key>" \
      -H "Content-Type: application/json" \
      -d '{
        "description": "search-only for products collection",
        "actions": ["documents:search"],
        "collections": ["products"]
      }'

    7. Backups

    Typesense supports on-demand snapshots:

    shell
    curl -X POST "http://localhost:8108/operations/snapshot?snapshot_path=/opt/typesense/backups/$(date +%F)" \
      -H "X-TYPESENSE-API-KEY: <admin-key>"

    Cron this nightly and rotate old snapshots (e.g., keep 7 days) — wire it into whatever backup pull/off-box process you already use for other RamNode internal service state.

    shell
    0 3 * * * curl -s -X POST "http://localhost:8108/operations/snapshot?snapshot_path=/opt/typesense/backups/$(date +\%F)" -H "X-TYPESENSE-API-KEY: <admin-key>" >> /var/log/typesense-snapshot.log 2>&1

    8. Common issues

    SymptomLikely causeFix
    Not Found on /health behind nginxMissing trailing slash or wrong proxy_pass targetConfirm proxy_pass http://127.0.0.1:8108; with no trailing path segment mismatch
    Cluster node won't joinPeering port blocked or IP list mismatch across nodesEnsure 8107 open between nodes and the nodes list/file is identical (order doesn't matter) on all three
    High memory usageFull index held in RAM by designRight-size the VPS RAM plan; there's no "disk-only" mode for the searchable index itself
    Writes slow in cluster modeNodes spread across regions with high inter-node latencyKeep Raft cluster members in the same DC; use single-node + async replication to other regions instead if you need geographic spread