TechnologieProgrammierung

Gemeinsame Meilisearch-Instanz für viele Content-Websites

netcomputing.de Redaktion26. August 202610 Min. Lesezeit

Zusammenfassung

Wer viele Content-Websites betreibt, braucht für jede eine schnelle, tippfehlertolerante Suche, aber eine Algolia-Instanz je Projekt summiert sich zu vielen Einzelabos. Meilisearch ist eine selbstgehostete Suchmaschine, bei der eine Instanz jede Website über einen eigenen, per API-Schlüssel abgesicherten Index bedient. Laut eigener Angabe liefert sie Treffer in unter 50 Millisekunden. Ein Schlüssel ohne passende Berechtigung scheitert mit einem klaren HTTP-Fehler statt falsch zu antworten.

Systemvoraussetzungen

Wer ein Portfolio aus zehn, zwanzig oder vierzig eigenständigen Content-Websites betreibt, braucht für jede davon eine Suche, die schon während des Tippens Ergebnisse zeigt und dabei Tippfehler verzeiht, statt nur exakte Treffer aus einer Datenbankabfrage mit LIKE zurückzugeben. Meilisearch ist eine quelloffene, selbstgehostete Suchmaschine, die genau dafür entwickelt wurde: schnelle, tippfehlertolerante, nach Relevanz sortierte Suche über eine REST-API, eine Programmierschnittstelle, die andere Programme per HTTP anspricht. Eine einzelne Meilisearch-Instanz bedient dabei beliebig viele Websites über getrennte Indexe, jeweils abgesichert mit einem eigenen, eingeschränkten API-Schlüssel, sodass keine Website eine eigene bezahlte Such-Instanz bei einem Anbieter wie Algolia braucht. Die folgende Tabelle nennt die Komponenten und Versionen, die dieser Aufbau voraussetzt.

KomponenteVersion / Anforderung
Meilisearch1.53.1 (aktueller Stable-Release, Docker-Image getmeili/meilisearch)
Docker EngineMit docker run oder Docker Compose steuerbar, Server hört standardmäßig auf Port 7700
MEILI_MASTER_KEYPflichtangabe im Produktivbetrieb, mindestens 16 Byte lang; darf ausschließlich serverseitig verwendet werden
Persistentes DatenverzeichnisVolume-Mount auf /meili_data, sonst gehen alle Indexe beim nächsten Container-Start verloren
Node.jsLTS-Version für das offizielle JavaScript-SDK meilisearch aus dem npm-Repository
API-Schlüssel je WebsiteErzeugt über POST /keys, beschränkt auf genau einen Index (eine benannte Dokumentensammlung) und genau die Aktionen, die diese Website braucht

Systemarchitektur

Ein Index ist in Meilisearch die Grundeinheit für durchsuchbare Daten: eine benannte Sammlung von Dokumenten mit einem gemeinsamen Schema, vergleichbar mit einer Tabelle in einer klassischen Datenbank. Für ein Portfolio aus mehreren Websites bekommt jede Website ihren eigenen Index, zum Beispiel website-a und website-b, auf demselben Meilisearch-Server. Ein Tech-Magazin mit mehreren Dutzend Fachartikeln, wie es netcomputing.de mit seinen aktuell 95 Artikeln ist, wäre ein plausibler Kandidat für genau diesen Aufbau, sobald mehrere solcher Magazine sich denselben Suchserver teilen. Die Trennung zwischen den Websites entsteht nicht durch getrennte Server, sondern durch getrennte Indexe und durch API-Schlüssel, die jeweils nur einen dieser Indexe und nur bestimmte Aktionen erlauben. Das folgende Diagramm zeigt, wie eine Änderung auf zwei verschiedenen Websites am Ende bei der jeweils eigenen Suchleiste ankommt, ohne dass eine Website je Zugriff auf die Daten der anderen bekommt.

flowchart TD
    A[Redaktion pflegt Inhalte auf Website A] --> B[Website A schreibt Dokumente in den Index website-a]
    C[Redaktion pflegt Inhalte auf Website B] --> D[Website B schreibt Dokumente in den Index website-b]
    B --> E[Gemeinsamer Meilisearch-Server]
    D --> E
    E --> F[Website A fragt nur den Index website-a ab, mit ihrem eigenen suchbeschraenkten Schluessel]
    E --> G[Website B fragt nur den Index website-b ab, mit ihrem eigenen suchbeschraenkten Schluessel]
    F --> H[Suchfeld auf Website A zeigt Treffer aus website-a laut Meilisearch in unter 50 Millisekunden]
    G --> I[Suchfeld auf Website B zeigt Treffer aus website-b laut Meilisearch in unter 50 Millisekunden]

Jede Suchanfrage läuft immer gegen genau einen Index, und die Rangfolge der Treffer wird deshalb pro Index unabhängig berechnet: Website A kann ihre Produktnamen höher gewichten als Website B ihre Artikelüberschriften, ohne dass sich die beiden Konfigurationen je berühren. Ohne eigene Konfiguration sortiert ein neu angelegter Index jeden Treffer nach sieben eingebauten Ranking-Regeln in dieser festen Reihenfolge: words (wie viele Suchbegriffe ein Dokument enthält), typo (wie wenige Tippfehler der Treffer aufweist), proximity (wie nah die Suchbegriffe im Dokument beieinanderstehen), attributeRank (wie wichtig das Feld ist, in dem der Treffer sitzt), sort (die zur Suchzeit übergebene Sortierung), wordPosition (wie früh im Feld der Treffer steht) und exactness (wie genau der Treffer dem Suchbegriff entspricht). Laut Meilisearchs eigener Einstellungs-Referenz ist exakt diese Reihenfolge der Standardwert für jeden neuen Index, und jede Website im Portfolio kann sie unabhängig von den anderen verändern. Der Schreibweg und der Leseweg laufen dabei über getrennte Schlüssel: Ein Redaktionssystem schreibt mit einem Schlüssel, der die Aktion documents.add für genau einen Index erlaubt, während die öffentliche Suchleiste im Browser einen zweiten Schlüssel verwendet, der ausschließlich die Aktion search für denselben Index erlaubt. Meilisearch selbst gibt in der eigenen Dokumentation an, dass jede Anfrage unabhängig von der Indexgröße in unter 50 Millisekunden beantwortet wird; das ist eine Herstellerangabe und kein unabhängig gemessener Benchmark, deckt sich aber mit dem erklärten Ziel des Produkts, sich wie eine Autovervollständigung anzufühlen statt wie eine klassische Suchergebnisseite.

Schritt-für-Schritt-Umsetzung

Docker startet den gemeinsamen Meilisearch-Server

Der Server läuft einmal für das gesamte Portfolio und muss dafür nicht für jede Website neu aufgesetzt werden. Das offizielle Docker-Image bringt den kompletten Suchserver als einzelnes Binary mit, das nach dem Start sofort seine REST-API auf Port 7700 bereitstellt.

docker run -d \
  --name meilisearch \
  -p 7700:7700 \
  -e MEILI_MASTER_KEY="ein-mindestens-16-byte-langer-master-schluessel" \
  -v $(pwd)/meili_data:/meili_data \
  getmeili/meilisearch:v1.53.1

Der Master-Schlüssel aus MEILI_MASTER_KEY ist Meilisearchs übergeordneter Administrationsschlüssel, mit dem sich weitere, eingeschränkte API-Schlüssel erzeugen lassen. Er darf ausschließlich serverseitig verwendet werden, etwa aus einem Deploy-Skript oder einer Backend-Route heraus, und gehört nie in eine Website, weder in serverseitigen noch in clientseitigen Code, weil er uneingeschränkten Zugriff auf jeden Index gibt.

Ein API-Schlüssel beschränkt sich auf einen Index und eine Handlung

Für jede angeschlossene Website entstehen mindestens zwei eigene API-Schlüssel über den Endpunkt POST /keys, aufgerufen mit dem Master-Schlüssel: ein Suchschlüssel für das Frontend und ein Schreibschlüssel für das Redaktionssystem. Beide erhalten ausschließlich die Rechte, die diese eine Website tatsächlich braucht, festgelegt über das Feld indexes für den betroffenen Index und das Feld actions für die erlaubte Handlung.

curl -X POST 'https://meilisearch.intern.example.de/keys' \
  -H 'Authorization: Bearer DEIN_MASTER_SCHLUESSEL' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "description": "Suchschluessel fuer website-a",
    "actions": ["search"],
    "indexes": ["website-a"],
    "expiresAt": null
  }'

Die Antwort liefert unter anderem die Felder uid und key; der Wert von key erscheint laut Meilisearchs eigener Dokumentation nur bei der Erzeugung ein einziges Mal und muss sofort sicher gespeichert werden. Ein Wert von null bei expiresAt erzeugt einen Schlüssel, der nie abläuft; für einen öffentlich sichtbaren Suchschlüssel ist ein festes Ablaufdatum die vorsichtigere Wahl. Für das Redaktionssystem derselben Website entsteht ein zweiter Schlüssel mit der Aktion documents.add statt search, ebenfalls beschränkt auf den Index website-a und diesmal mit einem festen Ablaufdatum.

curl -X POST 'https://meilisearch.intern.example.de/keys' \
  -H 'Authorization: Bearer DEIN_MASTER_SCHLUESSEL' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "description": "Schreibschluessel fuer website-a",
    "actions": ["documents.add"],
    "indexes": ["website-a"],
    "expiresAt": "2027-01-01T00:00:00Z"
  }'

Das offizielle JavaScript-SDK schreibt Dokumente in den Index einer Website

Das npm-Paket meilisearch ist Meilisearchs offizielles JavaScript-SDK und kapselt die REST-API in einem Client-Objekt. Ein Indexierungs-Skript verbindet sich mit dem Server, legt fest, welche Felder durchsuchbar, filterbar und sortierbar sind, und schreibt anschließend die Artikel einer Website in ihren eigenen Index.

import { MeiliSearch } from "meilisearch";

const client = new MeiliSearch({
  host: "https://meilisearch.intern.example.de",
  apiKey: process.env.MEILISEARCH_WRITE_KEY,
});

const index = client.index("website-a");

await index.updateSettings({
  searchableAttributes: ["title", "excerpt", "body"],
  filterableAttributes: ["category", "publishedYear"],
  sortableAttributes: ["publishedYear"],
});

await index.addDocuments([
  {
    id: "artikel-42",
    title: "Docker Compose fuer Redaktionsteams",
    excerpt: "Ein gemeinsamer Server fuer mehrere Websites.",
    body: "Volltext des Artikels ...",
    category: "Technologie",
    publishedYear: 2026,
  },
]);

Die Reihenfolge in searchableAttributes bestimmt zugleich, wie stark ein Treffer im jeweiligen Feld bei der Rangfolge zählt: Ein Treffer im zuerst genannten Feld wiegt schwerer als ein gleichwertiger Treffer im letzten. filterableAttributes und sortableAttributes bleiben ohne diese Einträge leer, und eine Suche, die auf ein nicht freigegebenes Feld filtert, schlägt dann mit einem Fehler fehl, nicht stillschweigend mit einem leeren Ergebnis.

Eine Next.js-Suchleiste fragt den eigenen Index mit einem suchbeschränkten Schlüssel ab

Im Frontend jeder Website verwendet die Suchleiste ausschließlich den zweiten, auf search beschränkten Schlüssel aus dem vorigen Schritt, niemals den Schreib- oder den Master-Schlüssel. Eine Client-Komponente ruft dafür bei jeder Eingabe die Methode search des Index auf.

"use client";

import { useState } from "react";
import { MeiliSearch } from "meilisearch";

const client = new MeiliSearch({
  host: "https://meilisearch.intern.example.de",
  apiKey: process.env.NEXT_PUBLIC_MEILISEARCH_SEARCH_KEY,
});

const index = client.index("website-a");

export default function ArtikelSuche() {
  const [treffer, setTreffer] = useState([]);

  async function suchen(begriff) {
    if (begriff.length === 0) {
      setTreffer([]);
      return;
    }
    const ergebnis = await index.search(begriff, { limit: 10 });
    setTreffer(ergebnis.hits);
  }

  return (
    <div>
      <input
        type="text"
        onChange={(event) => suchen(event.target.value)}
        placeholder="Artikel durchsuchen"
      />
      <ul>
        {treffer.map((hit) => (
          <li key={hit.id}>{hit.title}</li>
        ))}
      </ul>
    </div>
  );
}

Die Suchantwort enthält neben dem Feld hits mit den Treffern auch processingTimeMs, die Zeit, die der Server für genau diese eine Anfrage gebraucht hat. Weil NEXT_PUBLIC_MEILISEARCH_SEARCH_KEY ohnehin im ausgelieferten Browser-Code sichtbar wird, darf an dieser Stelle ausschließlich der suchbeschränkte Schlüssel stehen, niemals ein Schlüssel mit der Aktion documents.add oder gar der Master-Schlüssel.

Häufige Fehlerquellen

  • 403 mit dem Fehlercode invalid_api_key beim Schreiben mit dem Suchschlüssel. Ein Redaktions-Skript versucht, mit dem suchbeschränkten Schlüssel Dokumente per documents.add hinzuzufügen, und Meilisearch antwortet mit Status 403 und der Meldung „The provided API key is invalid.“. Fix: für Schreibvorgänge den zweiten, dafür erzeugten Schlüssel mit der Aktion documents.add verwenden und den Suchschlüssel ausschließlich im Frontend einsetzen.
  • Der Master-Schlüssel landet im ausgelieferten Browser-Code. Eine Variable wie NEXT_PUBLIC_MEILISEARCH_KEY bekommt versehentlich den Master-Schlüssel statt eines Suchschlüssels zugewiesen, wodurch jeder Websitebesucher über die Entwicklertools des Browsers vollen Schreibzugriff auf sämtliche Indexe bekäme. Fix: im Frontend ausnahmslos einen Schlüssel mit der einzigen Aktion search einsetzen, den Master-Schlüssel nur in serverseitigen Umgebungsvariablen ohne NEXT_PUBLIC_-Präfix halten.
  • invalid_search_filter beim Filtern nach einem nicht freigegebenen Feld. Eine Suche mit einem Filter auf category schlägt fehl, weil category noch nicht in filterableAttributes steht; Meilisearch nennt in der Fehlermeldung die tatsächlich erlaubten Felder. Fix: das Feld vorher per updateSettings in filterableAttributes aufnehmen und abwarten, bis Meilisearch den betroffenen Index neu aufgebaut hat.
  • Ein gemeinsamer Index ohne Website-Filter zeigt fremde Artikel an. Statt eines eigenen Index je Website teilen sich mehrere Websites einen einzigen Index mit einem gemeinsamen Feld wie site; fehlt der Filter auf dieses Feld in einer Suchanfrage, tauchen Treffer jeder angeschlossenen Website in jeder Suchleiste auf. Fix: entweder für jede Website einen eigenen Index führen statt eines gemeinsamen Index mit Anwendungsfilter, oder den Site-Filter serverseitig fest in jede Suchanfrage einbauen und nie aus einem Client-Parameter übernehmen.
  • Die Ranking-Regeln attribute und attributeRank/wordPosition gemeinsam gesetzt. Meilisearchs eigene Dokumentation nennt attribute eine ältere, zusammengefasste Regel, die genau das leistet, was attributeRank und wordPosition zusammen leisten; wer versucht, alle drei gleichzeitig in rankingRules einzutragen, bekommt von Meilisearch einen Konfigurationsfehler zurück. Fix: entweder die ältere Einzelregel attribute verwenden oder die beiden neueren Regeln attributeRank und wordPosition zusammen, nie gemischt.

Häufig gestellte Fragen

Wie trennt Meilisearch die Daten mehrerer Websites in einer Instanz?

Meilisearch trennt die Daten mehrerer Websites über getrennte Indexe innerhalb derselben Instanz, wobei jeder Index seine eigenen Dokumente, seine eigenen Sucheinstellungen und seine eigene Rangfolge der Treffer hat. Ein API-Schlüssel lässt sich beim Erzeugen über POST /keys im Feld indexes auf genau einen dieser Indexe beschränken, sodass ein Schlüssel für website-a technisch nicht auf die Dokumente von website-b zugreifen kann. Die Trennung besteht also aus zwei Schichten: dem Index als Datencontainer und dem Schlüssel als Zugriffsgrenze.

Was passiert, wenn ein suchbeschränkter API-Schlüssel Dokumente schreiben soll?

Ein suchbeschränkter API-Schlüssel, der nur die Aktion search erlaubt, kann laut Meilisearchs eigener Fehlerdokumentation keine Dokumente hinzufügen oder ändern. Der Versuch schlägt mit HTTP-Status 403 und dem Fehlercode invalid_api_key fehl, mit der Meldung „The provided API key is invalid.“. Für Schreibvorgänge wie documents.add braucht dieselbe Website einen zweiten, eigens dafür erzeugten Schlüssel mit genau dieser Aktion, sodass ein im Frontend sichtbarer Suchschlüssel nie zum Verändern von Inhalten missbraucht werden kann.

Wie tolerant ist Meilisearch gegenüber Tippfehlern in der Sucheingabe?

Meilisearch toleriert Tippfehler in der Sucheingabe abhängig von der Wortlänge, gesteuert über die Einstellung minWordSizeForTypos. In der Standardeinstellung erlaubt Meilisearch laut eigener Dokumentation einen Tippfehler ab fünf Zeichen Wortlänge und zwei Tippfehler ab neun Zeichen Wortlänge; kürzere Wörter werden nur bei exakter Schreibweise gefunden. Mehr als zwei Tippfehler pro Wort lässt Meilisearch unabhängig von der eingestellten Wortlänge nie zu, ein drittes falsches Zeichen führt also nie mehr zu einem Treffer.

Lohnt sich eine gemeinsame Meilisearch-Instanz auch für nur eine einzige Website?

Für nur eine einzige Website lohnt sich eine gemeinsame Meilisearch-Instanz in den meisten Fällen nicht in vollem Umfang, weil ihr eigentlicher Vorteil erst entsteht, sobald mehrere Websites sich denselben Server, denselben Betriebsaufwand und dieselbe Absicherung teilen. Eine einzelne Website profitiert zwar bereits von der schnellen, tippfehlertoleranten Suche, könnte diese aber ebenso gut über eine eigene kleine Meilisearch-Instanz beziehen, ohne die Indextrennung und die schlüsselbasierte Mandantentrennung aus diesem Aufbau zu brauchen. Der Aufwand für Docker-Betrieb, Backups und Versions-Updates bleibt in beiden Fällen ähnlich hoch.

Quellen

  1. Welcome to Meilisearch (architecture and performance overview), Meilisearch, abgerufen am 2026-08-26
  2. Using Meilisearch with Docker, Meilisearch, abgerufen am 2026-08-26
  3. Meilisearch v1.53.1 release, Meilisearch (GitHub), abgerufen am 2026-08-26
  4. Create an API key (POST /keys reference), Meilisearch, abgerufen am 2026-08-26
  5. Settings API reference (searchable/filterable/sortable attributes, default ranking rules), Meilisearch, abgerufen am 2026-08-26
  6. Typo tolerance settings (minWordSizeForTypos), Meilisearch, abgerufen am 2026-08-26
  7. Master key and API keys, Meilisearch, abgerufen am 2026-08-26
  8. Error codes reference (invalid_api_key, invalid_search_filter), Meilisearch, abgerufen am 2026-08-26

Der Content wurde mit Hilfe von KI erstellt, vor allem in der Recherche und Vorformulierung. Prüfung und Abnahme durch unsere Redaktion.