Skip to main content

Self-Hosting Guide: Deploy Meilisearch for Fast Search

·OSSAlt Team
meilisearchsearchself-hostingdockerguide

Self-Hosting Guide: Deploy Meilisearch for Fast Search

Meilisearch is the open source Algolia alternative — typo-tolerant, instant search that returns results in under 50ms. Self-hosting removes the 10K document limit on the free tier and eliminates per-search pricing.

Requirements

  • VPS with 1 GB RAM minimum (scale with index size)
  • Docker
  • Domain name (e.g., search.yourdomain.com)
  • 10+ GB disk (depends on data volume)

Step 1: Deploy with Docker

docker run -d \
  --name meilisearch \
  --restart unless-stopped \
  -p 7700:7700 \
  -v meili_data:/meili_data \
  -e MEILI_MASTER_KEY=your-master-key-min-16-chars \
  -e MEILI_ENV=production \
  getmeili/meilisearch:latest

Generate a master key:

openssl rand -hex 24

Step 2: Reverse Proxy (Caddy)

# /etc/caddy/Caddyfile
search.yourdomain.com {
    reverse_proxy localhost:7700
}
sudo systemctl restart caddy

Step 3: Get API Keys

Meilisearch auto-generates API keys from the master key:

# List API keys
curl -s https://search.yourdomain.com/keys \
  -H 'Authorization: Bearer your-master-key' | jq
KeyPurposeUse In
Default Search API KeySearch only (read)Frontend
Default Admin API KeyFull access (read/write)Backend
Master KeyManages all keysNever expose

Step 4: Create an Index and Add Documents

# Create index with primary key
curl -X POST 'https://search.yourdomain.com/indexes/products/documents' \
  -H 'Authorization: Bearer your-admin-api-key' \
  -H 'Content-Type: application/json' \
  --data-binary '[
    {
      "id": 1,
      "title": "Wireless Headphones",
      "description": "Noise-cancelling Bluetooth headphones",
      "category": "Electronics",
      "price": 79.99
    },
    {
      "id": 2,
      "title": "Mechanical Keyboard",
      "description": "Cherry MX Blue switches, RGB backlight",
      "category": "Electronics",
      "price": 129.99
    }
  ]'

Step 5: Configure Index Settings

# Set searchable attributes (order = priority)
curl -X PUT 'https://search.yourdomain.com/indexes/products/settings' \
  -H 'Authorization: Bearer your-admin-api-key' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "searchableAttributes": ["title", "description", "category"],
    "filterableAttributes": ["category", "price"],
    "sortableAttributes": ["price"],
    "displayedAttributes": ["title", "description", "category", "price"],
    "typoTolerance": {
      "enabled": true,
      "minWordSizeForTypos": { "oneTypo": 4, "twoTypos": 8 }
    },
    "pagination": { "maxTotalHits": 1000 }
  }'
# Basic search
curl 'https://search.yourdomain.com/indexes/products/search' \
  -H 'Authorization: Bearer your-search-api-key' \
  -H 'Content-Type: application/json' \
  --data-binary '{ "q": "headphons" }'
# Returns "Wireless Headphones" despite typo ✨

# Search with filters
curl 'https://search.yourdomain.com/indexes/products/search' \
  -H 'Authorization: Bearer your-search-api-key' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "q": "keyboard",
    "filter": "price < 150",
    "sort": ["price:asc"]
  }'

Step 7: Frontend Integration

JavaScript SDK:

npm install meilisearch
import { MeiliSearch } from 'meilisearch'

const client = new MeiliSearch({
  host: 'https://search.yourdomain.com',
  apiKey: 'your-search-api-key',  // Search key only!
})

const results = await client.index('products').search('headphones', {
  limit: 10,
  filter: ['category = Electronics'],
})

InstantSearch (Algolia-compatible UI):

npm install react-instantsearch @meilisearch/instant-meilisearch
import { InstantSearch, SearchBox, Hits } from 'react-instantsearch'
import { instantMeiliSearch } from '@meilisearch/instant-meilisearch'

const { searchClient } = instantMeiliSearch(
  'https://search.yourdomain.com',
  'your-search-api-key'
)

function SearchPage() {
  return (
    <InstantSearch indexName="products" searchClient={searchClient}>
      <SearchBox />
      <Hits hitComponent={Hit} />
    </InstantSearch>
  )
}

function Hit({ hit }) {
  return (
    <div>
      <h3>{hit.title}</h3>
      <p>{hit.description}</p>
      <span>${hit.price}</span>
    </div>
  )
}

Step 8: Keep Data in Sync

Option 1: Batch sync (cron)

# Export from your database and push to Meilisearch daily
*/15 * * * * /usr/local/bin/sync-search.sh

Option 2: Real-time sync (webhook/event)

// After creating/updating a product in your app
await meiliClient.index('products').updateDocuments([updatedProduct])

// After deleting
await meiliClient.index('products').deleteDocument(productId)

Option 3: Database trigger Use n8n or a custom webhook to sync on database changes.

Production Hardening

Docker Compose (recommended for production):

services:
  meilisearch:
    image: getmeili/meilisearch:latest
    container_name: meilisearch
    restart: unless-stopped
    ports:
      - "7700:7700"
    volumes:
      - meili_data:/meili_data
    environment:
      - MEILI_MASTER_KEY=your-master-key
      - MEILI_ENV=production
      - MEILI_MAX_INDEXING_MEMORY=1024Mb
      - MEILI_MAX_INDEXING_THREADS=2

volumes:
  meili_data:

Backups:

# Snapshot (built-in)
curl -X POST 'https://search.yourdomain.com/snapshots' \
  -H 'Authorization: Bearer your-master-key'

# Or backup the data volume
docker run --rm -v meili_data:/data -v /backups:/backup alpine \
  tar czf /backup/meili-$(date +%Y%m%d).tar.gz /data

Updates:

docker pull getmeili/meilisearch:latest
docker stop meilisearch && docker rm meilisearch
# Re-run the docker run command (data persists in volume)

Monitoring:

  • Health check: GET /health (returns { "status": "available" })
  • Stats: GET /stats (index sizes, document counts)
  • Monitor search latency (should be < 50ms)

Resource Usage

DocumentsRAMCPUDisk
1-100K512 MB1 core5 GB
100K-1M2 GB2 cores20 GB
1M-10M8 GB4 cores50 GB

Rule of thumb: Meilisearch needs ~2x your dataset size in RAM for optimal performance.

VPS Recommendations

ProviderSpec (500K docs)Price
Hetzner2 vCPU, 4 GB RAM€4.50/month
DigitalOcean2 vCPU, 4 GB RAM$24/month
Linode2 vCPU, 4 GB RAM$24/month

Compare search engines on OSSAlt — speed, features, and self-hosting options side by side.