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

5.0 KiB
Raw Blame History

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 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).

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:

    # /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:

# 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:
    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:

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.