Skip to content

How to Deploy Meilisearch on a VPS

Updated Sep 2026

verified on Ubuntu 26.04 · Sep 2026
We earn commissions when you shop through the links below. Full disclosure →

Run Meilisearch in production mode on your own VPS, with a master key, scoped API keys, HTTPS through Caddy, snapshots and the upgrade path.

Before you start
  • A VPS with at least 1 GB RAM (more if your indexes are large; indexes live on disk and are memory-mapped)
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
  • A domain you can point at the server (the API should only be exposed over HTTPS)
  • Docker Engine + Compose installed (see the base guide below)
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What Meilisearch is

Meilisearch is an MIT-licensed search engine with a REST API. You send it JSON documents and it returns typo-tolerant, ranked, filterable results fast enough for search-as-you-type. There is no admin UI to look after. You manage it entirely through the API, with a master key at the top of the permission tree.

It is usually compared with Typesense, and Meilisearch vs Typesense covers the differences. It is also the search engine many self-hosted apps expect you to provide.

Server sizing

Meilisearch is small at rest. On our test box (GCP e2-standard-2, Ubuntu 26.04), an empty instance idled at about 25 MB of RAM and used about 459 MB of disk for the image and data. That figure is misleading for real use. Meilisearch's memory use scales with the size of your indexes and the amount of indexing work, so size for your data, not the idle number:

  • 1 GB RAM: the catalog minimum. Enough for small indexes such as a docs site or a few thousand products.
  • 2–4 GB RAM: comfortable for a few hundred thousand documents and regular re-indexing.
  • Disk: indexes take several times the size of the raw JSON, and dumps and snapshots need room too. Start at 20–40 GB of SSD and watch it grow.

Prepare the server

This guide assumes Docker Engine and the Compose plugin are installed. If not, see Docker & Compose on Ubuntu. Only SSH and the web ports should be open. The Meilisearch port stays on loopback.

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw --force enable
sudo ufw status verbose
Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)
DigitalOceanalso works on
1 vCPU · 1 GB RAM · 25 GB SSD · $6.00/mo
Deploy on DigitalOcean → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install Meilisearch (Docker Compose)

Two upstream rules shape this install:

  • Pin a version. The Meilisearch docs advise against latest, because a database is only compatible with the version that created it.
  • Run in production mode with a master key. MEILI_ENV=production refuses to start without a master key of at least 16 bytes.

Generate the master key once and keep it in .env:

mkdir -p ~/meilisearch && cd ~/meilisearch
if [ ! -f .env ]; then
  echo "MEILI_MASTER_KEY=$(openssl rand -base64 32 | tr -d '/+=')" > .env
fi
chmod 600 .env
cd ~/meilisearch
cat > docker-compose.yml <<'YAML'
services:
  meilisearch:
    image: getmeili/meilisearch:v1.54.0
    container_name: meilisearch
    restart: unless-stopped
    environment:
      MEILI_ENV: production
      MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
      MEILI_NO_ANALYTICS: "true"
    volumes:
      - ./meili_data:/meili_data
    ports:
      # Loopback only: Caddy terminates TLS in front of it.
      - "127.0.0.1:7700:7700"
YAML
docker compose up -d

MEILI_NO_ANALYTICS turns off Meilisearch's anonymous telemetry. Remove it if you're happy to send usage data. Inside the container the working directory is /meili_data. The database is at /meili_data/data.ms, and dumps and snapshots are written next to it. Everything therefore lands in ~/meilisearch/meili_data on the host.

Check that it is up. /health needs no key:

for i in $(seq 1 30); do curl -sf http://127.0.0.1:7700/health && break; sleep 2; done; echo

API keys: don't ship the master key

The master key is for administration only. When Meilisearch starts with a master key, it creates default API keys, among them a default search key (search only, for client-side requests) and a default admin key (full API access for day-to-day admin work). List them with the master key:

cd ~/meilisearch
. ./.env
curl -s http://127.0.0.1:7700/keys -H "Authorization: Bearer $MEILI_MASTER_KEY" \
  | grep -o '"name":"[^"]*"'

Use the search key in browsers and front ends. It can only search. Use the admin key, or better a key scoped to specific indexes and actions created through POST /keys, in your backend's indexing code. Keep the master key on the server for creating and rotating keys. If a key leaks, delete it with DELETE /keys/{uid} and create a new one. Key values are derived from the master key, so changing the master key changes every key your clients use.

For multi-tenant search, where each user sees only their own documents, sign tenant tokens from a search key. They let you add filter rules without creating a key per user.

HTTPS + domain

Keys travel in the Authorization header, so only expose Meilisearch over HTTPS. Point an A record for search.example.com at the server and follow Automatic HTTPS with Caddy:

search.example.com {
    reverse_proxy 127.0.0.1:7700
}

Check it from your laptop:

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

If only your own backend talks to Meilisearch, you may not need a public hostname at all. Leave it on loopback, or on a private network between your servers, and skip the proxy.

Backups: snapshots and dumps

Meilisearch has two backup formats, and they do different jobs:

  • Snapshots are an exact copy of the database. They restore quickly, but only into the same Meilisearch version. Use them for routine backups.
  • Dumps are a version-independent export of documents, settings, keys and tasks. They restore slowly because everything is re-indexed. Use them to move between versions or machines.

Trigger a snapshot through the API. It is written to /meili_data/snapshots:

cd ~/meilisearch
. ./.env
curl -s -X POST http://127.0.0.1:7700/snapshots -H "Authorization: Bearer $MEILI_MASTER_KEY"; echo

The response is a task. When it reaches succeeded (check GET /tasks/{taskUid}), the file appears here:

sleep 5; ls -lh ~/meilisearch/meili_data/snapshots/ 2>/dev/null || echo "snapshot still running"

A dump works the same way with POST /dumps, and the file lands in /meili_data/dumps. Copy snapshots, dumps and .env off the server. To restore, start Meilisearch with --import-snapshot <path> or --import-dump <path>. For a dump, delete the old data.ms first. If you need the full commands, see the Meilisearch Docker docs.

Upgrades

This is the step that catches people. A Meilisearch database only opens in the version that created it, so changing the tag and restarting is not enough. Upstream's current procedure, available since v1.51:

  1. Take a snapshot (above) and wait for it to succeed.
  2. Stop Meilisearch.
  3. Change the image tag, and start it once with the --upgrade-db flag so it migrates the database on startup.
cd ~/meilisearch
docker compose down
# edit docker-compose.yml: image: getmeili/meilisearch:<new version>
docker compose run --rm -d --name meili-upgrade -p 127.0.0.1:7700:7700 \
  meilisearch meilisearch --upgrade-db

Once the upgrade finishes and /health answers, stop the one-off container and run docker compose up -d again. If Meilisearch refuses to upgrade in place, upstream's fallback is a dump: create it on the old version, then import it on the new one with --import-dump. Upgrades are not atomic, which is why the snapshot comes first.

Troubleshooting

The container exits straight away. Read docker compose logs meilisearch. In production mode, a missing master key or one shorter than 16 bytes is a fatal error at startup.

Requests return missing_authorization_header or invalid_api_key. In production mode every route except /health needs a key. Check which key your client sends and whether it has the right actions and indexes.

After an image bump: "database version is incompatible". You changed the tag without upgrading the database. Put the old tag back, then follow the upgrade steps above.

Indexing is slow or runs out of memory. Indexing is the expensive part. Send documents in batches, keep filterableAttributes and sortableAttributes to the fields you actually use, and add RAM before anything else.

Verification + next steps

You're done when:

  • https://search.example.com/health returns available;
  • your front end searches with the search key only;
  • your backend indexes with a scoped key;
  • a snapshot and a copy of .env sit off the server.

From there, set up ranking rules and synonyms for your data, or plug Meilisearch into apps that support it, such as Karakeep. To see the other main option, look at Typesense. For ranked hosts, see Best VPS for databases.

Next steps

How to self-host Meilisearch →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy BookStack on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy Directus on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokku on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Ghostfolio on a VPS →How to Deploy Gitea on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Memos on a VPS →How to Deploy n8n on a VPS →How to Deploy Navidrome on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Ollama on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenObserve on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plane on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy Pocket ID on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy Zabbix on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.