How to Deploy Meilisearch on a VPS
Updated Sep 2026
verified on Ubuntu 26.04 · Sep 2026Run Meilisearch in production mode on your own VPS, with a master key, scoped API keys, HTTPS through Caddy, snapshots and the upgrade path.
- 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)
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
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=productionrefuses 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:
- Take a snapshot (above) and wait for it to succeed.
- Stop Meilisearch.
- Change the image tag, and start it once with the
--upgrade-dbflag 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/healthreturnsavailable;- your front end searches with the search key only;
- your backend indexes with a scoped key;
- a snapshot and a copy of
.envsit 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.