Files
HiveGrid-Wireframes/hivegrid-cloud/docs/deploy/meilisearch.md
T

142 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Deploying Meilisearch
This document covers production deployment of the Meilisearch service that
backs `packages/search` and the `search-indexer` worker job. Local
development uses the bundled docker-compose service — see
`docker-compose.yml` and `docs/features/search.md` for the application-side
contract.
## Requirements
- Disk: 1–10 GiB per million indexed documents, SSD strongly recommended.
- Memory: 1 GiB minimum; allocate ~30 % of the dataset size for hot reads.
- Network: reachable only from `apps/web`, `apps/api` and the
`worker-general` service. **Never** expose port 7700 to the public
internet. The master key is the only auth boundary; if the port is open,
the index is open.
- TLS: terminate at the platform's ingress / load balancer; Meilisearch
itself does not speak TLS.
## Environment Variables
| Variable | Required | Purpose |
| --- | --- | --- |
| `MEILI_URL` | yes | URL the search wrapper and worker dial. Internal DNS only (e.g. `http://meilisearch.internal:7700`). |
| `MEILI_MASTER_KEY` | yes | Server-side secret. Min 32 bytes of entropy. Rotate via blue/green; never log. |
| `MEILI_ENV` | yes | `production` in production. Disables the unauthenticated dashboard. |
| `MEILI_NO_ANALYTICS` | recommended | Set to `true` to disable upstream telemetry. |
## Coolify (Hetzner) — recommended for the staging stack
Coolify ships a "custom docker-compose" service type that maps 1:1 onto
the local layout. Paste the snippet below into the project's
docker-compose override (Coolify → Service → Configuration → Compose).
```yaml
services:
meilisearch:
image: getmeili/meilisearch:v1.8
environment:
- MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
- MEILI_ENV=production
- MEILI_NO_ANALYTICS=true
volumes:
- meilisearch-data:/meili_data
healthcheck:
test: ["CMD-SHELL", "wget -q -O - http://127.0.0.1:7700/health | grep -q 'available'"]
interval: 10s
timeout: 5s
retries: 6
start_period: 15s
restart: unless-stopped
networks:
- backend
volumes:
meilisearch-data:
networks:
backend:
external: true
```
Operator steps:
1. Set `MEILI_MASTER_KEY` as a Coolify "secret" (Project → Variables →
"Encrypt"). Length ≥ 32 bytes; generate with `openssl rand -hex 32`.
2. Set `MEILI_URL=http://meilisearch:7700` on `apps/web`, `apps/api` and
the `worker-general` service so they resolve via the internal Docker
network. Do **not** map the port to the host.
3. Add a backup cron that snapshots the `meilisearch-data` volume:
```bash
# /etc/cron.daily/meilisearch-snapshot
docker exec meilisearch \
curl -X POST -H "Authorization: Bearer $MEILI_MASTER_KEY" \
http://127.0.0.1:7700/snapshots
```
4. Verify `/health` is `available` after deploy; the worker logs
`meilisearch.connected` on cold start.
## Dedizierte VM — alternative (Hetzner Cloud)
For traffic spikes that outgrow the central Coolify host, run Meilisearch on
a dedicated Hetzner Cloud VM (e.g. CX22) with a 10 GB volume:
```bash
# Hetzner: attach a 10 GB volume and mount at /meili_data
ssh root@<instance> docker run -d --name meilisearch \
--restart unless-stopped \
-p 7700:7700 \
-v /meili_data:/meili_data \
-e MEILI_MASTER_KEY="$(cat /etc/meilisearch/master-key)" \
-e MEILI_ENV=production \
-e MEILI_NO_ANALYTICS=true \
getmeili/meilisearch:v1.8
```
Front the instance with a private network; never expose
port 7700 to the public internet. Apps reach it via the private DNS name
configured in the `MEILI_URL` env.
## Master-Key Rotation
1. Generate a new key: `openssl rand -hex 32`.
2. Set the new key as `MEILI_MASTER_KEY_NEW` in the secret store.
3. Bring up a parallel Meilisearch container with the new key; replicate
indexes via the snapshot/restore endpoint:
```bash
curl -X POST -H "Authorization: Bearer $OLD_KEY" \
"$OLD_MEILI/snapshots"
# copy the snapshot file, start new container pointing at it
```
4. Flip `MEILI_URL` on the apps to point at the new container.
5. Decommission the old container; rotate the secret-store key (move
`MEILI_MASTER_KEY_NEW` to `MEILI_MASTER_KEY`).
The wrapper's transparent SQL-LIKE fallback (`searchWithFallback`) keeps
search degraded-but-up during the cutover.
## Disaster Recovery
The Postgres database remains the source of truth. To rebuild Meilisearch
from scratch:
```bash
pnpm cli search reindex --tenant=all --index=all
```
This writes `search.index.<entity>.bulkreindex` outbox events only for the
currently supported bulk-reindex set. Today `--index=all` expands to KB
articles, CRM contacts, rows, and users. Entities are rejected until their
tenant-scoped projection has an explicit pager design. The `search-indexer` job
consumes the accepted events and pages over Postgres in 1000-row chunks.
Expected throughput: ~10 k documents per minute per worker replica.
## Backup Retention
Snapshots are full copies of the data directory. Retain at least 7 daily
snapshots offsite (S3 / Backblaze). The volume itself is also covered by
the platform's nightly volume backup if you keep that enabled.