Meilisearch is what happens when someone writes Algolia's user-facing model as a single Rust binary. Run it on a $5 VPS, index records via HTTP, query in milliseconds. Open source; same UI library as Algolia.
Two ways to run Meilisearch: self-host (free, you run the box) or Meilisearch Cloud β (paid, they run it). This tutorial covers self-hosting.
Single binary:
curl -L https://install.meilisearch.com | sh
./meilisearch --master-key="your-strong-master-key"
Listens on http://localhost:7700. The master key is required in production; without one, Meilisearch starts in dev mode (no auth). Generate a long random string for real use.
Docker alternative:
docker run -it --rm \
-p 7700:7700 \
-v $(pwd)/meili_data:/meili_data \
getmeili/meilisearch:latest \
meilisearch --master-key="your-strong-master-key"
Test it:
curl http://localhost:7700/health
# {"status":"available"}
Spin up a $5/month box on DigitalOcean, Hetzner, etc. SSH in and run:
# Install Meilisearch
curl -L https://install.meilisearch.com | sh
sudo mv ./meilisearch /usr/local/bin/
# Run as a systemd service
sudo nano /etc/systemd/system/meilisearch.service
Paste:
[Unit]
Description=Meilisearch
After=systemd-user-sessions.service
[Service]
Type=simple
ExecStart=/usr/local/bin/meilisearch --config-file-path /etc/meilisearch.toml
Restart=on-failure
[Install]
WantedBy=multi-user.target
Create /etc/meilisearch.toml with your master key, data path, environment, etc. (see configuration docs β).
sudo systemctl enable meilisearch
sudo systemctl start meilisearch
Put Nginx + HTTPS in front. Now you have a real search endpoint at https://search.yourdomain.com.
The master key is for admin operations only β don't ship it. Use scoped API keys for your app.
Auto-generated on first run: query the keys endpoint with the master key:
curl -X GET 'http://localhost:7700/keys' \
-H 'Authorization: Bearer your-master-key'
Two default keys are pre-generated:
MEILI_HOST=https://search.yourdomain.com
MEILI_MASTER_KEY=your-master-key # backend admin ops only
MEILI_ADMIN_KEY=xxxxxxxxxx # backend; from Default Admin API Key
MEILI_SEARCH_KEY=xxxxxxxxxx # safe for client; from Default Search API Key
npm install meilisearch
Official SDKs: JS, Python, Ruby, PHP, Go, Java, .NET, Rust, Swift, Dart β.
import { MeiliSearch } from "meilisearch";
const client = new MeiliSearch({
host: process.env.MEILI_HOST,
apiKey: process.env.MEILI_ADMIN_KEY,
});
// Add documents (creates the index automatically)
await client.index("posts").addDocuments([
{
id: 1, // required; primary key
title: "How to deploy to Vercel",
body: "First, install the CLI...",
tags: ["deploy", "vercel"],
published_at: 1714521600,
},
{ id: 2, title: "...", body: "..." },
]);
Meilisearch processes async β returns a task ID. For batch imports of millions of records, poll /tasks/{id} for completion. For small adds, results are searchable in milliseconds.
Two settings that matter most (mirroring Algolia):
// Which fields are searchable, in priority order
await client.index("posts").updateSearchableAttributes([
"title",
"body",
"tags",
]);
// Which fields you can filter on
await client.index("posts").updateFilterableAttributes(["tags", "author"]);
// Custom ranking
await client.index("posts").updateRankingRules([
"words", "typo", "proximity", "attribute",
"sort", "exactness",
"published_at:desc", // newer first
]);
import { MeiliSearch } from "meilisearch";
const client = new MeiliSearch({
host: process.env.NEXT_PUBLIC_MEILI_HOST,
apiKey: process.env.NEXT_PUBLIC_MEILI_SEARCH_KEY, // search-only; safe
});
const results = await client.index("posts").search("vercel", {
limit: 10,
filter: ['tags = "deploy"'],
});
Use the search-only key. It can only read, not modify; safe to ship in browser.
Meilisearch ships an InstantSearch-compatible adapter, so all the Algolia UI widgets work:
npm install instantsearch.js @meilisearch/instant-meilisearch
# or for React:
npm install react-instantsearch @meilisearch/instant-meilisearch
import { InstantSearch, SearchBox, Hits } from "react-instantsearch";
import { instantMeiliSearch } from "@meilisearch/instant-meilisearch";
const { searchClient } = instantMeiliSearch(
process.env.NEXT_PUBLIC_MEILI_HOST,
process.env.NEXT_PUBLIC_MEILI_SEARCH_KEY,
);
export function Search() {
return (
<InstantSearch searchClient={searchClient} indexName="posts">
<SearchBox />
<Hits hitComponent={({ hit }) => <div>{hit.title}</div>} />
</InstantSearch>
);
}
Same patterns as Algolia:
addDocuments. Simple, doubles latency slightly.For Postgres β Meilisearch, Airbyte β has a free connector. Or use Supabase's Meilisearch wrapper β if you're on Supabase.
// Update (merges with existing fields by primary key)
await client.index("posts").updateDocuments([
{ id: 1, body: "Updated body" },
]);
// Delete
await client.index("posts").deleteDocument(1);
await client.index("posts").deleteDocuments({ filter: 'tags = "draft"' });
// Stats
const stats = await client.index("posts").getStats();
console.log(stats.numberOfDocuments);
// Drop the whole index
await client.deleteIndex("posts");
Meilisearch produces a single dump file you can restore from:
curl -X POST 'http://localhost:7700/dumps' \
-H 'Authorization: Bearer your-master-key'
Dumps land in your configured data path. Schedule via cron; rsync to S3 / R2 for offsite copies. Restore by starting Meilisearch with --import-dump path/to/dump.dump.
Meilisearch can rebuild from your source-of-truth DB in minutes β for many teams, "the DB is the backup; just re-sync to Meilisearch on disaster" works fine.
Meilisearch Cloud β hosts the same engine you'd self-host. $30/mo for the starter tier. Worth it if you don't want to maintain a Linux box; still much cheaper than Algolia at meaningful scale.
Migration is symmetric β dump from self-hosted, restore to Cloud, switch your client's host URL.