Meilisearch für schnelle Suche selbst hosten
Folge dem auf deinem eigenen Server. Kostenlos starten, keine Karte nötig
Algolia und ähnliche gehostete Such-APIs rechnen nach Suchvolumen und Datensatzanzahl ab, was sich schnell summiert, sobald eine Website echten Traffic hat. Meilisearch ist eine in Rust geschriebene Open-Source-Suchmaschine, die tippfehlertolerante, sofortige Suche über deine eigenen Daten indiziert, und leicht genug, um auf dem billigsten Server zu laufen, den ein Cloud-Anbieter verkauft. Am Ende dieser Anleitung läuft sie hinter HTTPS, geschützt mit einem echten Master-Key, mit einem Snapshot, aus dem du wiederherstellen kannst, und einem kurzen Upgrade-Pfad.
Was du brauchst
- Einen Server mit Docker und dem Compose-Plugin. Ein Hetzner CX23 (2 vCPU, 4 GB RAM, 40 GB NVMe, 5,99 €/Monat exkl. MwSt.) betreibt Meilisearch für einen kleinen bis mittleren Index komfortabel.
- Eine Domain oder Subdomain, die auf die IP-Adresse des Servers zeigt.
- SSH-Zugang zum Server.
- Deine Daten bereit zum Indizieren: Meilisearch hält Dokumente, die du ihm schickst, es crawlt nichts von sich aus.
Schritt 1: Einen echten Master-Key generieren
Meilisearch verweigert den Start in Produktion ohne einen. MEILI_ENV=production verlangt einen Key von mindestens 16 Byte, und ohne ihn (oder mit einem kürzeren) loggt der Server einen vorgeschlagenen Key und beendet sich, statt ungeschützt zu laufen:
openssl rand -base64 48
Setze das Ergebnis in eine .env-Datei neben deiner Compose-Datei, nicht in die Versionskontrolle eingecheckt:
MEILI_MASTER_KEY=paste-the-generated-value-here
chmod 600 .env
Schritt 2: Die Docker-Compose-Datei schreiben
services:
meilisearch:
image: getmeili/meilisearch:v1.55.0
restart: unless-stopped
environment:
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
MEILI_ENV: production
MEILI_NO_ANALYTICS: "true"
volumes:
- ./meili_data:/meili_data
expose:
- "7700"
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- meilisearch
volumes:
caddy_data:
caddy_config:
Diese Anleitung pinnt v1.55.0, das aktuelle stabile Release zum Zeitpunkt der Erstellung. MEILI_ENV=production macht zwei Dinge: Es deaktiviert die eingebaute Such-Spielwiese-UI am Root-Pfad (eine Annehmlichkeit für die lokale Entwicklung, die auf einem öffentlichen Server nichts zu suchen hat), und es ist das, was den Master-Key zwingend statt optional macht. MEILI_NO_ANALYTICS=true nimmt die Instanz aus Meilisearchs eigener anonymer Nutzungsstatistik heraus. ./meili_data ist ein einfacher Bind-Mount statt ein benanntes Volume, speziell damit ein Backup in Schritt 5 nur das Kopieren eines Ordners ist, und hier liegt tatsächlich jeder Index und jedes Task-Log.
Prüfen, ob sie korrekt parst:
docker compose config
Schritt 3: DNS setzen und Caddy hinzufügen
Lege einen A-Record für deine Subdomain an, der auf die IP des Servers zeigt, dann füge eine Caddyfile neben compose.yml hinzu:
search.yourdomain.com {
reverse_proxy meilisearch:7700
}
Schritt 4: Den Stack starten und deinen ersten Index erstellen
docker compose up -d
docker compose ps
curl https://search.yourdomain.com/health
Du solltest beide Dienste als Up sehen und der Health-Check sollte {"status":"available"} zurückgeben. Erstelle einen Index und füge ein Dokument hinzu, um zu bestätigen, dass der Master-Key tatsächlich funktioniert:
curl -X POST 'https://search.yourdomain.com/indexes' \
-H "Authorization: Bearer ${MEILI_MASTER_KEY}" \
-H 'Content-Type: application/json' \
--data-binary '{ "uid": "movies", "primaryKey": "id" }'
curl -X POST 'https://search.yourdomain.com/indexes/movies/documents' \
-H "Authorization: Bearer ${MEILI_MASTER_KEY}" \
-H 'Content-Type: application/json' \
--data-binary '[{ "id": 1, "title": "Carol" }]'
Du solltest von beiden Aufrufen eine taskUid zurückbekommen. Prüfe, dass der Task fertig ist und dass eine Suche das Dokument tatsächlich findet:
curl "https://search.yourdomain.com/indexes/movies/search?q=carol" \
-H "Authorization: Bearer ${MEILI_MASTER_KEY}"
Du solltest "title": "Carol" im hits-Array sehen. Von hier an sollte deine Anwendung pro Anwendungsfall einen eingegrenzten, nicht-master API-Key erzeugen (einen, der nur suchen, nicht Indizes verwalten kann) über den /keys-Endpunkt, statt den Master-Key selbst an einen Browser zu schicken.
Schritt 5: /meili_data sichern
Meilisearchs eigener /snapshots-Endpunkt (eigentlich ein geplanter Dump, kein Live-Snapshot-API-Aufruf) schreibt eine .snapshot-Datei innerhalb des Containers in einem von dir konfigurierten Intervall, aber das einfachste und portabelste Backup ist ein Tarball des Volumes selbst, während der Container nicht aktiv schreibt:
cat > /opt/meilisearch/backup.sh <<'EOF'
#!/bin/sh
cd /opt/meilisearch
docker compose stop meilisearch
tar czf backups/meili-$(date +%F).tar.gz meili_data
docker compose start meilisearch
find backups -name '*.tar.gz' -mtime +14 -delete
EOF
mkdir -p /opt/meilisearch/backups
chmod +x /opt/meilisearch/backup.sh
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/meilisearch/backup.sh") | crontab -
Führe es einmal händisch aus und prüfe, ob es funktioniert hat: ./backup.sh && ls -lh backups. Du solltest eine .tar.gz-Datei mit dem heutigen Datum sehen. Das kurze Stoppen und Starten hält das Archiv intern konsistent; für einen öffentlich erreichbaren Such-Index sind ein paar Sekunden Nichtverfügbarkeit um 3 Uhr morgens ein vernünftiger Tausch für ein Backup, dem du tatsächlich vertrauen kannst. Kopiere die Datei auch vom Server weg, damit der Verlust der Box nicht auch den Verlust des Index bedeutet.
Schritt 6: Upgrade
docker compose pull meilisearch
docker compose up -d meilisearch
Meilisearch migriert sein eigenes Datenformat auf der Festplatte automatisch beim Start, wenn möglich, aber die eigenen Release Notes des Projekts weisen auf Versionssprünge hin, die eine Neu-Indizierung über einen Dump-und-Import statt ein In-Place-Upgrade verlangen; lies das Changelog für die Version, zu der du wechselst, bevor du mehrere Versionen auf einmal überspringst, besonders über eine Major-Version-Grenze hinweg.
Die Abkürzung: mit Railyard
Railyard liefert Meilisearch als One-Click-Anwendungs-Template (meilisearch im Template-Katalog), gebaut aus dem offiziellen getmeili/meilisearch-Image, mit bereits gesetztem MEILI_NO_ANALYTICS und einem fertig verdrahteten persistenten Daten-Volume. Deploye es direkt: app.railyard.run/deploy?template=meilisearch. Ein echter Unterschied zum manuellen Weg oben: Das Template folgt derzeit dem schwimmenden latest-Tag statt einer gepinnten Version, wo diese Anleitung dich ein bestimmtes Release selbst wählen und upgraden lässt, bewegt sich Railyard automatisch mit Upstream mit; setze MEILI_MASTER_KEY in beiden Fällen selbst als Variable auf der Settings-Seite der App, er wird nicht für dich generiert. Siehe mit einem Button deployen dafür, wie One-Click-Templates allgemein funktionieren, und Datenbanken und Backups dafür, wie persistente Volumes auf der Plattform gehandhabt werden.
Troubleshooting
Der Container beendet sich sofort mit einer Meldung über den Master-Key. MEILI_ENV=production ohne gesetztes MEILI_MASTER_KEY, oder mit einem unter 16 Byte, lässt Meilisearch den Start verweigern statt offen zu laufen. Prüfe erneut, dass die .env-Datei aus Schritt 1 tatsächlich einen echten Wert hat und dass docker compose config ihn aufgelöst zeigt, keinen leeren String.
401 Unauthorized bei jedem API-Aufruf. Der Authorization: Bearer <key>-Header fehlt, oder du übergibst den falschen Key; denk daran, dass der Master-Key alles kann, einschließlich andere Keys zu erzeugen, aber jede Anfrage braucht ihn trotzdem (oder einen eingegrenzten Key) im Header; es gibt keinen unauthentifizierten Pfad, sobald ein Master-Key gesetzt ist.
HTTPS wird nicht ausgestellt. Prüfe docker compose logs caddy auf den ACME-Fehler; es ist fast immer der DNS-Record aus Schritt 3, der noch nicht auf diesen Server zeigt, bestätige mit dig +short search.yourdomain.com.
Suchergebnisse enthalten ein Dokument nicht, das du gerade hinzugefügt hast. Indizierung ist asynchron; jeder Schreibvorgang gibt eine taskUid zurück, und das Dokument ist nicht durchsuchbar, bis dieser Task fertig ist. Fragen /tasks/{taskUid} ab, wenn du den Abschluss programmatisch bestätigen musst, statt mit einer festen Verzögerung zu raten.
Speicher wird knapp auf einem kleinen Server mit einem großen Index. Meilisearch hält eine bedeutende Menge an Index-Daten im Speicher für Abfragegeschwindigkeit; die 4 GB eines CX23 reichen für einen moderaten Katalog, aber ein Index mit mehreren Millionen Dokumenten will den CX33 (8 GB) oder größer statt Swap, da die Suchlatenz (nicht nur Stabilität) leidet, sobald geswappt wird.
FAQ
Ist Meilisearch ein Ersatz für Elasticsearch? Für die meisten Anwendungsfälle von Anwendungssuche (ein E-Commerce-Katalog, eine Doku-Seite, In-App-Suche), ja, und mit deutlich weniger operativem Aufwand; Elasticsearch gewinnt immer noch bei Log-Aggregation und komplexen Analyse-Workloads, für die Meilisearch nie gebaut wurde.
Können mehrere Apps sich eine Meilisearch-Instanz teilen? Ja, Indizes sind innerhalb derselben Instanz nach uid namensräumlich getrennt, und eingegrenzte API-Keys können einen bestimmten Key auf bestimmte Indizes beschränken, sodass eine Instanz sicher mehr als eine Anwendung bedient.
Brauche ich zusätzlich eine separate Datenbank? Nein, Meilisearch ist das System of Record für das, was du in es hinein indizierst, obwohl die meisten Setups es als abgeleiteten Such-Index behandeln, der mit einer primären Datenbank synchron gehalten wird, statt als einzige Kopie der Daten.
Wie groß kann ein Index auf einem CX23 werden? Es gibt keine harte Grenze in Meilisearch selbst; die praktische Obergrenze sind RAM und Festplatte des Servers, ein paar Hunderttausend Dokumente typischer Größe sind auf 4 GB komfortabel, deutlich darüber lohnt sich das Hochskalieren wirklich.
Ist die REST-API der einzige Weg hinein? Nein, es gibt offizielle SDKs für JavaScript, Python, PHP, Ruby, Go, Rust und mehr, die alle dieselbe REST-API aufrufen, die diese Anleitung direkt mit curl nutzt.
Sobald der Master-Key, HTTPS und ein Backup eingerichtet sind, läuft Meilisearch größtenteils von selbst. Die zwei Dinge, die es sich lohnt später zu überdenken, sind ein eingegrenzter Key pro Anwendung statt des Master-Keys überall, und der Backup-Plan aus Schritt 5, sobald der Index etwas ist, das du tatsächlich vermissen würdest.
Warum Leute sich dafür Railyard entscheiden
- Dein eigenes Cloud-Konto. Deploye auf DigitalOcean, Hetzner, Vultr, AWS, OVHcloud oder jedem Linux-Server per SSH, direkt von deinem Anbieter abgerechnet, ohne Aufschlag.
- Ein fester Preis pro Team. Free ist für immer kostenlos: ein Server plus ein Datenbankserver, drei Apps, keine Karte. Pro kostet 19 $ im Monat für bis zu fünf Server und unbegrenzte Apps. Team kostet 49 $ im Monat mit unbegrenzten Servern. Keine Kosten pro App, pro Deployment, pro Sitz oder pro Gigabyte.
- Templates mit einem Klick. Deploye Open-Source-Apps, wie Datenbanken, Analytics und Dev-Tools, mit einem Klick auf deinem eigenen Server.
- Verwaltete Datenbanken. Postgres, MySQL und Redis mit geplanten, wiederherstellungsgeprüften Backups und Point-in-Time-Recovery.
- Push-to-Deploy. Deploye von GitHub, GitLab oder der CLI, mit automatischem HTTPS und Releases ohne Ausfallzeit.