Skip to Content

Production Guide: Deploy Meilisearch with Kubernetes + Helm + ingress-nginx on Ubuntu

A practical production runbook for secure Meilisearch deployment, scaling, verification, and recovery.

Search becomes a bottleneck quickly once product catalogs, documentation, or support content grow past a few thousand records. A common pattern we see is teams starting with database LIKE queries, then struggling with latency spikes, weak typo tolerance, and expensive query plans as traffic rises. This guide shows a production-ready way to deploy Meilisearch on Ubuntu with Kubernetes, Helm, and ingress-nginx so your application gets fast full-text search, predictable operations, and a clean upgrade path.

The real-world use case: you run a SaaS app with docs, product pages, and user-generated knowledge base entries. You need low-latency search APIs, typo tolerance, faceting, and robust backups, while keeping secrets out of Git and ensuring you can recover cleanly after node or disk failures. We will deploy Meilisearch with persistent storage, TLS termination, resource limits, secure API keys, verification checks, and a practical troubleshooting workflow.

Architecture and flow overview

We will run Meilisearch as a Helm release in Kubernetes, expose it through ingress-nginx over HTTPS, and store indexes on a persistent volume. Requests flow from clients to ingress-nginx, then to the Meilisearch service inside the cluster. Backups are performed by periodic index snapshots and copied to object storage or an external backup target.

  • Kubernetes namespace: isolated meilisearch namespace
  • Deployment method: Helm chart with values override
  • Ingress: ingress-nginx + TLS secret
  • Storage: PVC-backed data directory for durability
  • Security: master key from Kubernetes Secret, network policy optional
  • Operations: readiness/liveness checks, metrics/log inspection, controlled upgrades

Why this pattern works in production: Helm gives repeatable deployments, Kubernetes gives scheduling and recovery primitives, ingress-nginx centralizes HTTPS and routing, and PVC-backed storage decouples search state from pod lifecycle. That combination keeps day-2 operations manageable for small teams.

Prerequisites

  • Ubuntu host with a working Kubernetes cluster (single-node lab or multi-node production)
  • kubectl access with cluster-admin privileges for initial setup
  • helm v3 installed locally
  • ingress-nginx controller installed and reachable
  • A DNS record (for example search.example.com) pointing to your ingress endpoint
  • TLS certificate (cert-manager recommended, or pre-created TLS secret)
kubectl version --short
helm version
kubectl get pods -n ingress-nginx
kubectl get ingressclass

Manual copy fallback: if the copy button does not work in your browser/editor, select the full command block and copy with Ctrl/Cmd + C.

Step-by-step deployment

1) Create namespace and secret

Use a dedicated namespace so quotas, policies, and lifecycle are easier to manage. Generate a strong Meilisearch master key and store it as a Kubernetes Secret. Never hardcode this key in Helm values tracked by Git.

kubectl create namespace meilisearch
export MEILI_MASTER_KEY="$(openssl rand -hex 32)"
kubectl -n meilisearch create secret generic meilisearch-secrets \
  --from-literal=MEILI_MASTER_KEY="$MEILI_MASTER_KEY"

Manual copy fallback: if JavaScript is stripped by the platform, copy the commands manually from this block.

2) Add Helm repository and inspect defaults

Before installing, always review default chart values. This helps avoid surprises around persistence, probes, and security settings.

helm repo add meilisearch https://meilisearch.github.io/meilisearch-kubernetes
helm repo update
helm show values meilisearch/meilisearch > /tmp/meilisearch-default-values.yaml

Manual copy fallback: highlight the commands and copy manually if the button is unavailable.

3) Create production values file

This values file enables persistence, constrains resources, wires the master key secret, and exposes Meilisearch through ingress-nginx with TLS. Adjust storage class and host to match your environment.

cat > /tmp/meilisearch-values.yaml <<'EOF'
image:
  tag: v1.8

env:
  - name: MEILI_ENV
    value: production
  - name: MEILI_MASTER_KEY
    valueFrom:
      secretKeyRef:
        name: meilisearch-secrets
        key: MEILI_MASTER_KEY

persistence:
  enabled: true
  size: 40Gi
  accessMode: ReadWriteOnce

resources:
  requests:
    cpu: "500m"
    memory: "1Gi"
  limits:
    cpu: "2"
    memory: "4Gi"

service:
  type: ClusterIP
  port: 7700

ingress:
  enabled: true
  className: nginx
  hosts:
    - host: search.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: meilisearch-tls
      hosts:
        - search.example.com

probes:
  liveness:
    enabled: true
  readiness:
    enabled: true
EOF

Manual copy fallback: copy and paste this block exactly, then edit hostnames and storage values before applying.

4) Install (or upgrade) the release

Use helm upgrade --install so the same command supports first deploy and repeatable updates. This keeps your runbook simple and automation-friendly.

helm upgrade --install meilisearch meilisearch/meilisearch \
  -n meilisearch \
  -f /tmp/meilisearch-values.yaml

kubectl -n meilisearch get pods
kubectl -n meilisearch get svc
kubectl -n meilisearch get ingress

Manual copy fallback: if copy control fails, copy line-by-line; preserve the backslashes for multi-line commands.

5) Create scoped API keys for apps

Do not use the master key in your frontend or day-to-day service calls. Use the master key only to generate scoped keys with minimum needed permissions. In production, place generated keys in your application secret store rather than source files.

MASTER_KEY=$(kubectl -n meilisearch get secret meilisearch-secrets \
  -o jsonpath='{.data.MEILI_MASTER_KEY}' | base64 -d)

curl -sS -X POST "https://search.example.com/keys" \
  -H "Authorization: Bearer ${MASTER_KEY}" \
  -H "Content-Type: application/json" \
  --data '{
    "description": "search-only key for web app",
    "actions": ["search"],
    "indexes": ["*"],
    "expiresAt": null
  }'

Manual copy fallback: copy the command manually if your editor strips interactive buttons.

Configuration and secrets handling best practices

Meilisearch performs best when its role is clear: a search engine fed by your system of record, not your source of truth. Keep indexing jobs deterministic and idempotent. Push normalized documents from your app or data pipeline and version your index settings so ranking changes are auditable.

For secrets, store only references in Helm values and put actual credentials in Kubernetes Secrets or an external secret manager. Rotate the master key on a planned interval, reissue scoped keys, and document client cutover steps. If your organization supports external secrets controllers, use those to avoid manual key handling entirely.

Resource tuning matters. Underprovisioned memory causes instability during indexing bursts, while no CPU limits can create noisy-neighbor effects. Start with conservative requests/limits and tune using real traffic profiles. Enable snapshots or regular exports and copy them off-cluster. A backup inside the same failure domain is not a recovery strategy.

Network posture should be explicit. If only internal services should query search, add a NetworkPolicy and restrict ingress paths. If internet-facing, enforce TLS, consider IP allowlists for admin endpoints, and monitor unusual query patterns. Most production incidents are not from one giant failure; they are from small, unobserved drifts over time.

Verification checklist

Run these checks immediately after deployment and after every chart upgrade:

  • Pod is Running and readiness probe is healthy
  • Ingress resolves over HTTPS with the expected certificate
  • Health endpoint responds quickly
  • Create index and search flow works with scoped key
  • PVC is bound and data survives pod restart
kubectl -n meilisearch rollout status deploy/meilisearch
kubectl -n meilisearch get pvc

curl -sS https://search.example.com/health

kubectl -n meilisearch delete pod -l app.kubernetes.io/name=meilisearch
kubectl -n meilisearch get pods -w

Manual copy fallback: copy these commands manually if the one-click control is unavailable.

Common issues and fixes

Issue: Ingress returns 404 or default backend

Cause: wrong host, ingress class mismatch, or path not bound to controller.

Fix: verify ingressClassName, DNS record, and controller namespace logs. Confirm the host in your values file exactly matches public DNS.

Issue: 401/403 errors from application queries

Cause: app is using master key incorrectly, expired key, or action scope mismatch.

Fix: regenerate scoped keys with correct actions and indexes. Keep master key server-side only.

Issue: Slow indexing or high latency during reindex

Cause: insufficient CPU/memory or too-large indexing batches.

Fix: increase resource limits in values, reduce batch size, and schedule heavy indexing during off-peak windows.

Issue: Data lost after restart

Cause: persistence disabled, wrong mount path, or unbound PVC.

Fix: confirm persistence.enabled=true, PVC bound status, and storage class compatibility in your cluster.

FAQ

Can I run Meilisearch without ingress and only internal service access?

Yes. For private microservice usage, skip ingress and expose only ClusterIP. Use an internal gateway or service mesh for auth and mTLS if required.

How often should I rotate the master key?

A practical baseline is quarterly, or immediately after any suspected secret exposure. Rotation should include regeneration of all scoped keys and a staged client rollout plan.

What is the safest way to perform upgrades?

Take a snapshot, test the chart and image tag in staging, then run helm upgrade in production with a maintenance window and rollback criteria documented in advance.

Do I need a separate database for Meilisearch?

No. Meilisearch stores its own index data in its data directory. Your primary database remains the source of truth; Meilisearch serves search and ranking workloads.

How should I design indexing pipelines?

Use idempotent jobs with deterministic IDs. Reindex incrementally when possible, and keep schema/ranking settings in version-controlled config so changes are traceable.

How do I handle multi-tenant search securely?

Use tenant-scoped API keys and index filters, avoid broad wildcard permissions, and enforce server-side authorization checks before issuing search tokens to clients.

What should I monitor first in production?

Start with p95/p99 query latency, indexing duration, pod restarts, CPU/memory pressure, and ingress error rates. These signals catch most problems early.

Related internal guides

Talk to us

If you want this implemented with hardened defaults, observability, and tested recovery playbooks, our team can help.

Contact Us

How to Deploy Vaultwarden with Docker Compose and Traefik (Production Guide)
Production-focused setup with TLS, secret hygiene, backups, and verifiable operations checks.