How to Install Typesense on Ubuntu 24.04 VPS: Open-Source Instant Search (Algolia Alternative)
Typesense is an open-source, typo-tolerant search engine designed to deliver sub-50ms instant search for e-commerce catalogs, blogs, SaaS dashboards, and documentation sites. This guide walks you through a production-grade install on Ubuntu 24.04 LTS: from APT repository setup and systemd tuning through collections, scoped API keys, a 3-node high-availability cluster, and an Nginx TLS front door. By the end you will have a self-hosted Algolia replacement serving live queries over HTTPS.
Looking for a lightweight starting point? The CloudCore Starter VPS gives you enough headroom to run a single-node Typesense install comfortably for small-to-medium datasets.
Table of Contents
What is Typesense?
Typesense is an open-source search engine released under the GPL v3 license. It is written in C++, keeps its primary index in memory for predictable low-latency responses, and is designed to be as easy to operate as a managed service like Algolia. You POST JSON documents, you define a schema, and you query a REST API -- that is the whole mental model.
Typesense ships with the features most teams end up bolting onto Elasticsearch or OpenSearch: native typo tolerance with configurable edit distance, prefix search for as-you-type interfaces, faceting, dynamic filtering, geo-search, curated results (pinning and hiding), synonyms, analytics, and multi-field ranking. It exposes an OpenAPI-documented REST API and maintains official client libraries for JavaScript, TypeScript, Python, PHP, Ruby, Go, Java, and Swift, plus an InstantSearch.js adapter so front-end code written for Algolia works with a few lines of configuration changes.
Typical use cases include:
- E-commerce product search with facets (category, brand, price ranges), typo-tolerant queries, and sort-by-popularity
- SaaS application search across tickets, users, projects, and documents, with scoped keys per tenant
- Documentation search for developer portals, with weighted ranking by section type and recency
- Marketplace and listings search with geo-filtering ("within 5 km of lat/lng")
- Log and audit search for small-to-medium volumes where Elasticsearch would be overkill
- AI-augmented semantic search via Typesense's built-in vector search support, paired with embedding models
Why Self-Host Typesense?
Moving your search off a hosted platform like Algolia onto your own VPS delivers meaningful, measurable benefits:
- Flat, predictable pricing -- Algolia charges per record and per operation. At 500K records with 200K searches/month you are easily paying USD 300-500/month. A self-hosted Typesense node on a EUR 7.99/month VPS delivers the same workload at a fraction of the cost, with unlimited operations.
- Data sovereignty and GDPR compliance -- Your users' search queries and indexed content never leave your infrastructure. For EU-regulated businesses, hosting in-region on a known VPS removes an entire category of data-processing agreements and SCC paperwork.
- No operation quotas -- Hosted search platforms meter everything. On your own server you can reindex thousands of times per day, run large batch imports, or spike to tens of thousands of QPS with no throttling beyond what your hardware provides.
- Full feature set, no paywalls -- Open-source Typesense exposes every feature (synonyms, curation, analytics, vector search, multi-search). Nothing is gated behind a pricing tier.
- Low latency -- When your application and Typesense share a VPN or a private network, p99 latency drops to single-digit milliseconds because you eliminate the round-trip to a third-party edge.
- Customization -- Run custom build flags, mount indices on NVMe, tune Raft parameters, patch the source for niche requirements. None of this is possible on a SaaS.
- Portability -- The entire dataset lives in a
/var/lib/typesense/datadirectory you control. Snapshots, backups, cloning, and migrations are straightforward tar/rsync operations.
Cost Comparison
| Scenario | Algolia | ElasticCloud | Self-Hosted Typesense |
|---|---|---|---|
| 100K records, 50K searches/mo | ~USD 50/mo | ~USD 95/mo | EUR 7.99/mo (CloudCore Starter) |
| 500K records, 200K searches/mo | ~USD 300/mo | ~USD 125/mo | EUR 7.99-11.99/mo |
| 2M records, 1M searches/mo | ~USD 800/mo | ~USD 250/mo | EUR 19.99/mo (Professional) |
| Data leaves your infra? | Yes | Yes | No |
| Per-op charges? | Yes | No | No |
Prerequisites
Before you begin, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access
- SSH access to your server
- At least 2 GB of RAM (4 GB recommended for datasets over 500K documents)
- At least 10 GB of free disk space for the data directory and snapshots
- A domain name (optional, needed only for Nginx TLS in Step 11)
Recommended Plan: CloudCore Starter>
For single-node Typesense installs handling up to a few hundred thousand documents, the CloudCore Starter plan is a strong baseline:>
- 2 vCPU cores
- 4 GB RAM
- 40 GB NVMe SSD
- Unmetered bandwidth
- From EUR 7.99/month>
Scale up to Professional or Enterprise tiers when your index exceeds a few million records or when you build a 3-node HA cluster.
Connect to your server:
ssh root@your-server-ipStep 1: Update System Packages
Refresh the package index and apply any pending updates:
sudo apt update && sudo apt upgrade -yInstall the utilities you will need for the rest of the guide:
sudo apt install -y curl gnupg ca-certificates jq ufwIf the kernel updated, reboot:
sudo rebootStep 2: Install Typesense (APT Method)
Typesense publishes a signed APT repository at dl.typesense.org. This is the preferred method for bare-metal and VPS installs because you get a proper systemd unit, managed configuration at /etc/typesense/, and automatic security updates if unattended-upgrades is enabled.
Import the GPG key
curl -fsSL https://dl.typesense.org/apt/keyring.gpg | sudo gpg --dearmor -o /usr/share/keyrings/typesense-archive-keyring.gpgAdd the repository
echo "deb [signed-by=/usr/share/keyrings/typesense-archive-keyring.gpg] https://dl.typesense.org/apt/ stable main" | sudo tee /etc/apt/sources.list.d/typesense.listInstall the server
sudo apt update
sudo apt install -y typesense-serverThe installer places the binary at /opt/typesense-server/typesense-server, drops a default config at /etc/typesense/typesense-server.ini, creates a typesense system user, and registers a systemd unit.
Verify the install:
typesense-server --versionExpected output:
typesense-server 28.0Skip to Step 4 if you installed via APT.
Step 3: Install Typesense (Docker Method)
If you prefer Docker -- for reproducible deployments, easier version pinning, or to slot Typesense into an existing Compose stack -- use the official typesense/typesense image.
Install Docker
curl -fsSL https://get.docker.com | sudo sh
sudo systemctl enable --now dockerCreate data and config directories
sudo mkdir -p /var/lib/typesense/data /etc/typesenseCreate a Docker Compose file
sudo tee /etc/typesense/docker-compose.yml > /dev/null <<'EOF'
services:
typesense:
image: typesense/typesense:28.0
container_name: typesense
restart: unless-stopped
ports:
- "127.0.0.1:8108:8108"
volumes:
- /var/lib/typesense/data:/data
command: >
--data-dir /data
--api-key=REPLACE_WITH_A_STRONG_RANDOM_STRING
--listen-address=0.0.0.0
--listen-port=8108
--enable-cors
EOFGenerate a strong API key and drop it into the file:
API_KEY=$(openssl rand -hex 32)
sudo sed -i "s/REPLACE_WITH_A_STRONG_RANDOM_STRING/$API_KEY/" /etc/typesense/docker-compose.yml
echo "Your Typesense admin API key: $API_KEY"Save this key somewhere safe -- you will use it for every admin call.
Start the container
cd /etc/typesense && sudo docker compose up -dCheck health:
curl http://localhost:8108/healthExpected output:
{"ok":true}Docker users can skip to Step 6.
Step 4: Configure typesense-server.ini
The APT install drops a template at /etc/typesense/typesense-server.ini. Open it with your editor:
sudo nano /etc/typesense/typesense-server.iniReplace the contents with a production-ready configuration:
[server]api-key = REPLACE_WITH_A_STRONG_RANDOM_STRING
data-dir = /var/lib/typesense/data
log-dir = /var/log/typesense
api-address = 0.0.0.0
api-port = 8108
listen-address = 127.0.0.1
listen-port = 8108
enable-cors = true
Raft / cluster (single-node defaults; we revisit in Step 10)
peering-address = 127.0.0.1
peering-port = 8107Resource limits
max-memory-ratio = 0.75Snapshots
snapshot-interval-seconds = 3600Key settings explained:
api-key-- The bootstrap admin key. Treat it like a root password. Generate withopenssl rand -hex 32.data-dir-- Where Typesense persists the index, WAL, and snapshots. Put this on NVMe if you can.listen-address+listen-port-- Bind for the HTTP API. Keep it on127.0.0.1:8108and front it with Nginx (Step 11) for production. Set to0.0.0.0only if you are running a cluster without a reverse proxy and trust the network.api-port-- The HTTP port Typesense listens on for client requests. Same aslisten-portin most deployments.peering-address+peering-port-- Raft replication endpoint. We tune this in Step 10.max-memory-ratio-- Typesense will refuse writes when this fraction of system RAM is exceeded.0.75is a safe default.enable-cors-- Required if the browser calls Typesense directly (InstantSearch, Algolia-compatible widgets).
API_KEY=$(openssl rand -hex 32)
sudo sed -i "s/REPLACE_WITH_A_STRONG_RANDOM_STRING/$API_KEY/" /etc/typesense/typesense-server.ini
echo "Your Typesense admin API key: $API_KEY"
sudo chown typesense:typesense /etc/typesense/typesense-server.ini
sudo chmod 600 /etc/typesense/typesense-server.iniCreate the log directory:
sudo mkdir -p /var/log/typesense
sudo chown typesense:typesense /var/log/typesenseStep 5: Enable the systemd Service
The APT package installs /lib/systemd/system/typesense-server.service. Inspect it:
systemctl cat typesense-serverExpected content (abbreviated):
[Unit] Description=Typesense Server After=network.target[Service] User=typesense Group=typesense Type=simple ExecStart=/opt/typesense-server/typesense-server --config=/etc/typesense/typesense-server.ini Restart=on-failure LimitNOFILE=65535
[Install] WantedBy=multi-user.target
Enable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable --now typesense-serverVerify it is running:
sudo systemctl status typesense-serverExpected output:
● typesense-server.service - Typesense Server
Loaded: loaded (/lib/systemd/system/typesense-server.service; enabled)
Active: active (running) since Thu 2026-04-16 10:00:00 UTC; 5s ago
Main PID: 1234 (typesense-serve)
Tasks: 12
Memory: 58.2M
CGroup: /system.slice/typesense-server.service
└─1234 /opt/typesense-server/typesense-server --config=/etc/typesense/typesense-server.iniSmoke test the API:
curl http://localhost:8108/healthExpected output:
{"ok":true}For convenience, export the API key into your shell so the rest of the guide reads cleanly:
export TYPESENSE_API_KEY="paste-your-api-key-here"Step 6: Create Your First Collection
A collection in Typesense is analogous to a table in a relational database or an index in Elasticsearch. You define its schema up front: field names, types, and whether each field is facetable, indexed, or sortable.
Create a products collection for an e-commerce catalog:
curl -X POST "http://localhost:8108/collections" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "products",
"fields": [
{ "name": "name", "type": "string", "facet": false, "index": true, "sort": false },
{ "name": "description", "type": "string", "facet": false, "index": true, "sort": false },
{ "name": "brand", "type": "string", "facet": true, "index": true, "sort": false },
{ "name": "categories", "type": "string[]", "facet": true, "index": true },
{ "name": "price", "type": "float", "facet": true, "index": true, "sort": true },
{ "name": "in_stock", "type": "bool", "facet": true, "index": true },
{ "name": "rating", "type": "float", "facet": false, "index": true, "sort": true },
{ "name": "created_at", "type": "int64", "facet": false, "index": true, "sort": true },
{ "name": "location", "type": "geopoint", "facet": false, "index": true }
],
"default_sorting_field": "rating"
}'Expected output:
{
"name": "products",
"num_documents": 0,
"fields": [ ... ],
"default_sorting_field": "rating",
"created_at": 1768636800
}Schema field options explained
type-- Supported scalars:string,int32,int64,float,bool,geopoint. Array variants append[](e.g.string[]). Useautofor dynamically typed fields when the schema is not fully known upfront.facet-- Set totrueto enable facet counts on this field. Required for drop-down filters, category trees, and price buckets.index-- Set totrue(default) to make the field searchable or filterable. Set tofalsefor fields you only want to return in results but never query against, which saves memory.sort-- Set totrueon numeric fields you plan to sort by.stringfields require"sort": trueexplicitly if you want alphabetical ordering.optional-- Set totrueif a field may be missing from some documents.
curl "http://localhost:8108/collections" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" | jqStep 7: Index Documents
Typesense supports single-document inserts (POST /collections/<name>/documents) and bulk imports via JSONL (POST /collections/<name>/documents/import). Bulk import is dramatically faster and is what you want for initial loads and reindexing.
Single document
curl -X POST "http://localhost:8108/collections/products/documents" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "sku-1001",
"name": "CloudCore Starter VPS",
"description": "Entry-level NVMe VPS, 2 vCPU, 4 GB RAM, unmetered bandwidth.",
"brand": "vps-server.host",
"categories": ["vps", "cloud", "starter"],
"price": 5.99,
"in_stock": true,
"rating": 4.7,
"created_at": 1768636800,
"location": [52.52, 13.405]
}'Bulk import (JSONL)
Create a file products.jsonl where each line is a single JSON object:
cat > products.jsonl <<'EOF'
{"id":"sku-1002","name":"CloudCore Professional","description":"6 vCPU, 12 GB RAM","brand":"vps-server.host","categories":["vps","pro"],"price":19.99,"in_stock":true,"rating":4.9,"created_at":1768636900,"location":[52.52,13.405]}
{"id":"sku-1003","name":"CloudCore Enterprise","description":"12 vCPU, 32 GB RAM","brand":"vps-server.host","categories":["vps","enterprise"],"price":49.99,"in_stock":true,"rating":5.0,"created_at":1768637000,"location":[52.52,13.405]}
{"id":"sku-1004","name":"GPU AI Node","description":"NVIDIA A30, 24 GB VRAM","brand":"vps-server.host","categories":["gpu","ai"],"price":149.99,"in_stock":false,"rating":4.8,"created_at":1768637100,"location":[52.52,13.405]}
EOFImport with ?action=upsert so re-runs overwrite rather than error:
curl -X POST "http://localhost:8108/collections/products/documents/import?action=upsert" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
-H "Content-Type: text/plain" \
--data-binary @products.jsonlExpected output (one JSON object per line):
{"success":true}
{"success":true}
{"success":true}Typesense's bulk import handles tens of thousands of documents per second on modest hardware. For very large catalogs, chunk the input into 10K-50K document batches.
Step 8: Run Searches
Every search hits GET /collections/<name>/documents/search. The essential query parameters are:
q-- The user's search string. Use*to match everything (useful for pure browse/filter queries).query_by-- Comma-separated list of fields to search against. Order matters for default ranking weights.filter_by-- Boolean filter expression:price:<50 && in_stock:true && categories:=vps.facet_by-- Comma-separated facetable fields to compute counts for.sort_by-- Ordering spec:rating:desc,price:asc. Use_text_match:descto sort by relevance.
A simple typo-tolerant search
curl "http://localhost:8108/collections/products/documents/search?q=proffesional&query_by=name,description" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" | jq '.hits[].document.name'Expected output (note the typo in "proffesional" is corrected):
"CloudCore Professional"Filter + facet + sort
curl -G "http://localhost:8108/collections/products/documents/search" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
--data-urlencode "q=*" \
--data-urlencode "query_by=name" \
--data-urlencode "filter_by=price:<100 && in_stock:true" \
--data-urlencode "facet_by=brand,categories" \
--data-urlencode "sort_by=rating:desc,price:asc" \
--data-urlencode "per_page=10" | jqExpected output (abbreviated):
{
"found": 2,
"hits": [
{ "document": { "name": "CloudCore Enterprise", "price": 49.99, "rating": 5.0 } },
{ "document": { "name": "CloudCore Professional", "price": 19.99, "rating": 4.9 } }
],
"facet_counts": [
{ "field_name": "brand", "counts": [ { "value": "vps-server.host", "count": 2 } ] },
{ "field_name": "categories", "counts": [ { "value": "vps", "count": 2 }, { "value": "pro", "count": 1 } ] }
],
"search_time_ms": 2
}Geo-search
Find products within 50 km of Berlin:
curl -G "http://localhost:8108/collections/products/documents/search" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
--data-urlencode "q=*" \
--data-urlencode "query_by=name" \
--data-urlencode "filter_by=location:(52.52,13.405,50 km)" \
--data-urlencode "sort_by=location(52.52,13.405):asc"Federated (multi-collection) search
POST to /multi_search with an array of searches to run multiple queries in a single round trip -- perfect for global search bars that surface products, articles, and users at once.
curl -X POST "http://localhost:8108/multi_search" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"searches": [
{ "collection": "products", "q": "vps", "query_by": "name,description" },
{ "collection": "articles", "q": "vps", "query_by": "title,body" }
]
}'Step 9: Generate Scoped Search API Keys
Admin API keys let clients do anything: create collections, import documents, drop data. You never ship one to a browser. Instead, generate a dedicated search-only key, then derive scoped keys with embedded filters per tenant or per user.
Create a search-only parent key
curl -X POST "http://localhost:8108/keys" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "Search-only key for frontend",
"actions": ["documents:search"],
"collections": ["products", "articles"]
}'Expected output:
{
"id": 1,
"actions": ["documents:search"],
"collections": ["products","articles"],
"description": "Search-only key for frontend",
"value": "SEARCH_ONLY_KEY_ABCDEF123..."
}Store the full value -- Typesense only returns the plaintext key once; subsequent GET /keys only returns a key prefix.
Derive a scoped key client-side
Scoped keys are HMAC-SHA256 signatures of a JSON blob of additional constraints, base64-encoded alongside the parent key's prefix. They are generated in your application (backend preferred, even browsers work) without calling Typesense.
Node.js example:
import crypto from 'crypto';function generateScopedKey(parentKey, params) { const paramsJson = JSON.stringify(params); const digest = crypto .createHmac('sha256', parentKey) .update(paramsJson) .digest('base64'); const keyPrefix = parentKey.substring(0, 4); return Buffer.from(digest + keyPrefix + paramsJson).toString('base64'); }
const scopedKey = generateScopedKey( 'SEARCH_ONLY_KEY_ABCDEF123...', { filter_by: 'tenant_id:=acme-corp', expires_at: Math.floor(Date.now() / 1000) + 3600 // 1 hour } );
console.log(scopedKey);
Ship scopedKey to the browser in place of the raw search key. Typesense validates the HMAC on every request and enforces filter_by: tenant_id:=acme-corp automatically -- the user cannot escape their tenant, and the key expires in one hour.
This mechanism is what makes Typesense viable for multi-tenant SaaS: one collection, per-tenant scoped keys, zero data leakage.
Step 10: Configure a 3-Node High-Availability Cluster
Typesense uses Raft consensus for replication. A 3-node cluster tolerates one node failure while maintaining write quorum; a 5-node cluster tolerates two. Always run an odd number of nodes.
Assume you have three VPS instances on a private network:
node1-- 10.0.0.11node2-- 10.0.0.12node3-- 10.0.0.13
Install Typesense on all three nodes
Repeat Steps 1-5 on each node. Use the same api-key in every typesense-server.ini.
Create the shared nodes file
On every node, create /etc/typesense/nodes:
sudo tee /etc/typesense/nodes > /dev/null <<'EOF'
10.0.0.11:8107:8108,10.0.0.12:8107:8108,10.0.0.13:8107:8108
EOF
sudo chown typesense:typesense /etc/typesense/nodesThe format is <peering-address>:<peering-port>:<api-port>, comma-separated.
Update typesense-server.ini on every node
Edit /etc/typesense/typesense-server.ini on each node. The only value that differs per node is peering-address (and api-address if you expose the API):
node1 (10.0.0.11):
[server]
api-key = THE_SHARED_ADMIN_KEY
data-dir = /var/lib/typesense/data
log-dir = /var/log/typesense
api-address = 0.0.0.0
api-port = 8108
peering-address = 10.0.0.11
peering-port = 8107
nodes = /etc/typesense/nodes
enable-cors = trueRepeat for node2 with peering-address = 10.0.0.12 and node3 with peering-address = 10.0.0.13.
Open firewall ports between nodes
Allow peering (8107) and API (8108) traffic between the three VPS IPs. With UFW on node1:
sudo ufw allow from 10.0.0.12 to any port 8107,8108 proto tcp
sudo ufw allow from 10.0.0.13 to any port 8107,8108 proto tcpRepeat symmetrically on node2 and node3.
Restart all three nodes
sudo systemctl restart typesense-serverVerify cluster health
From any node:
curl "http://localhost:8108/debug" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" | jqYou should see "state": 1 on the leader and "state": 4 on followers. Check the leader's log for quorum:
sudo journalctl -u typesense-server -n 30 --no-pager | grep -i "elected\|leader"Writes sent to any node are forwarded to the leader and replicated. Reads can be served by any node. Put a load balancer (Nginx upstream with least_conn or a dedicated TCP LB) in front for client-facing traffic.
Step 11: Front Typesense with Nginx + TLS
For any production deployment where clients reach Typesense over the internet, terminate TLS at Nginx. This gives you HTTPS, optional rate limiting, request logging, and the ability to strip or inject headers.
Install Nginx and Certbot
sudo apt install -y nginx certbot python3-certbot-nginxPoint DNS
Create an A record for search.yourdomain.com that points to your VPS IP (or to your load balancer for the HA cluster).
Create the Nginx configuration
sudo tee /etc/nginx/sites-available/typesense > /dev/null <<'EOF'Rate limit zone: 100 requests per second per IP, burst of 200
limit_req_zone $binary_remote_addr zone=typesense_api:10m rate=100r/s;upstream typesense_backend { # For a single-node install, list localhost server 127.0.0.1:8108;
# For an HA cluster, list all three nodes: # server 10.0.0.11:8108; # server 10.0.0.12:8108; # server 10.0.0.13:8108; keepalive 32; }
server { listen 80; server_name search.yourdomain.com; return 301 https://$host$request_uri; }
server { listen 443 ssl http2; server_name search.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/search.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/search.yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=63072000" always; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY;
client_max_body_size 100m;
location / { limit_req zone=typesense_api burst=200 nodelay;
proxy_pass http://typesense_backend; proxy_http_version 1.1; 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_set_header Connection "";
proxy_buffering off; proxy_read_timeout 120s; proxy_send_timeout 120s; } } EOF
sudo ln -s /etc/nginx/sites-available/typesense /etc/nginx/sites-enabled/ sudo nginx -t
Issue the certificate
sudo certbot --nginx -d search.yourdomain.com
sudo systemctl reload nginxCertbot will auto-renew via a systemd timer. Confirm with systemctl list-timers | grep certbot.
Test HTTPS end-to-end
curl "https://search.yourdomain.com/collections" \
-H "X-TYPESENSE-API-KEY: $TYPESENSE_API_KEY" | jqYou now have Typesense behind HTTPS with rate limiting, ready for browser-direct search calls using a scoped API key.
Performance Tuning
- Pin the data directory to NVMe -- Typesense writes the Raft WAL on every mutation. Slow disks cap write throughput fast.
- Increase
ulimit -n-- The systemd unit ships withLimitNOFILE=65535. Verify withcat /proc/$(pgrep typesense-server)/limits. - Raise
max-memory-ratio-- If your VPS is dedicated to Typesense, bump it to0.85to let the index use more RAM. - Use
query_by_weights-- When searching multiple fields, weight them explicitly:query_by=name,description&query_by_weights=4,1. - Cache with
use_cache=true-- Typesense has an in-process result cache keyed on query parameters. Enable it for high-frequency, identical searches. - Batch writes -- Always prefer
/documents/importover per-document POSTs. Even 100x batching dramatically reduces CPU per document. - Snapshot off-peak -- Tune
snapshot-interval-secondsor trigger manual snapshots via/operations/snapshot?snapshot_path=/var/lib/typesense/snapshots/$(date +%F)during low-traffic windows. - Vertical before horizontal -- A single well-provisioned 16 GB RAM box serves most applications. Move to a cluster for fault tolerance, not for throughput.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
curl: (7) Failed to connect to localhost port 8108 | Service not running or bound to a different interface | sudo systemctl status typesense-server; verify listen-address in ini file |
{"message":"Forbidden - a valid x-typesense-api-key header must be sent."} | Missing or wrong admin key | Confirm api-key in /etc/typesense/typesense-server.ini matches the header you sent |
Imports hang or return 503 Service Unavailable | max-memory-ratio threshold exceeded | Add RAM, drop unused collections, or increase the ratio in config |
| Cluster stuck electing a leader | Nodes cannot reach each other on port 8107 | Check firewall and ping between peering addresses; review logs for "timeout" |
{"message":"Not ready or in maintenance mode"} | Replica is catching up via snapshot install | Wait; monitor with curl /debug on the follower until it reports healthy |
| Search returns zero hits despite matching documents | Field has "index": false or schema mismatch | Inspect schema: curl /collections/<name>; reindex with corrected field definitions |
| High p99 latency on geo queries | Missing radius filter combined with huge dataset | Always constrain geo searches with a reasonable km radius in filter_by |
Inspect logs
# Live tail
sudo journalctl -u typesense-server -fLast 100 lines
sudo journalctl -u typesense-server -n 100 --no-pagerRaft-specific
sudo journalctl -u typesense-server | grep -i raftFAQ
Is Typesense a drop-in replacement for Algolia?
For most applications, yes. Typesense matches Algolia on typo tolerance, faceting, filtering, sorting, grouping, synonyms, curation, geo-search, and multi-index federated search. The official typesense-instantsearch-adapter package lets you reuse Algolia InstantSearch widgets with a five-line configuration swap. Gaps tend to be in managed-infrastructure conveniences (global edge CDN, dashboard analytics depth) which you replace with your own VPS footprint plus something like Umami or Plausible for analytics.
How much RAM do I need?
Typesense holds the active index in memory, so RAM is the dominant sizing factor. A practical rule of thumb: 1 GB of RAM per 1 million small documents (a product row, a short article), scaled up linearly with average document size. For a 500K-product catalog with long descriptions, plan on 4 GB. Run load tests with realistic documents before sizing for production; the built-in /metrics endpoint reports resident set size per collection.
Should I use APT or Docker?
APT wins on simplicity for dedicated VPS installs: clean systemd integration, standard config paths, zero Docker overhead, and easy integration with unattended-upgrades. Docker wins when Typesense is one service among many in an existing Compose or Kubernetes stack, when you need reproducible version pinning across dev/staging/prod, or when you want lightweight isolation from the host. Both deliver identical runtime behavior -- pick the one that matches your operational model.
How does Typesense compare to Meilisearch, Elasticsearch, and OpenSearch?
Meilisearch sits closest to Typesense in design philosophy: fast, typo-tolerant, developer-friendly, schema-optional. Typesense tends to edge out on raw throughput and mature clustering; Meilisearch has a more polished dashboard. See our Meilisearch install guide for a side-by-side setup.
Elasticsearch is vastly more powerful but also more complex, more resource-hungry, and licensed under Elastic License 2.0. Use it when you need log aggregation at scale, custom analyzers, or deep query DSL. Our Elasticsearch install guide covers a production setup.
OpenSearch is the AWS-led Apache 2.0 fork of Elasticsearch. Similar feature set to Elasticsearch, different license. The OpenSearch install guide walks through a cluster deploy.
For most product search, site search, and application search workloads, Typesense or Meilisearch is the pragmatic choice. Reach for OpenSearch/Elasticsearch when you need log analytics or complex aggregations.
Can Typesense do vector / semantic search?
Yes. Typesense supports native vector fields and k-NN search. You either supply embeddings from your own model or configure Typesense to call a remote embedding API (OpenAI, Cohere, or a self-hosted model via an HTTP endpoint). This lets you do hybrid search: lexical query_by + vector similarity on the same collection with a single request.
Is Typesense Cloud the same as self-hosted?
Typesense Cloud is the managed offering from the Typesense team. It runs the same open-source binary you are installing here. Choosing self-hosted gives you lower cost, data sovereignty, and unconstrained tuning. Typesense Cloud gives you managed backups, monitoring, and multi-region. The codebase is identical, so migration in either direction is straightforward.
Next Steps
Now that Typesense is running on your VPS, here is where to go next:
- Build a live InstantSearch UI -- Pair your scoped search-only key with the Typesense InstantSearch adapter to ship a facet sidebar, sort dropdown, and pagination in an afternoon.
- Index your existing data -- Write a nightly sync script that pulls from your primary database (Postgres, MySQL) and
upserts into Typesense. The Typesense docs on syncing cover CDC-friendly patterns. - Add analytics -- Enable Typesense's built-in search analytics to track top queries, zero-result queries, and click-through rates. Feed this into your product roadmap.
- Set up automated backups -- Configure daily snapshots via
/operations/snapshot, push them off-box to S3 or a separate storage VPS, and test restore quarterly. - Upgrade to HA when your SLA demands it -- One-node Typesense is fast and reliable, but HTTP is a single point of failure. Step 10 in this guide has the 3-node blueprint when you are ready.
- Explore alternatives -- Compare with Meilisearch, Elasticsearch, and OpenSearch before committing long-term.
Deploy a Typesense-Ready VPS in Under 60 Seconds>
Spin up a fresh Ubuntu 24.04 VPS on the CloudCore Starter plan, follow this guide, and have typo-tolerant instant search running before your coffee gets cold.>
- 2 vCPU / 4 GB RAM / 40 GB NVMe
- Unmetered bandwidth, EU + North America locations
- From EUR 7.99/month, no long-term contract>
Launch a CloudCore Starter VPS