- Before you start
- Step 1: Prepare the system
- Step 2: Create a dedicated user
- Step 3: Download and install Meilisearch
- Step 4: Generate a master key
- Step 5: Create the configuration file
- Step 6: Create a systemd service
- Step 7: Verify Meilisearch is running
- Step 8: Configure the firewall
- Step 9: Expose it to the internet via Nginx (only if WordPress is on a different server)
- Step 10: Create the index
- Step 11: Create scoped API keys
- Step 12: Configure Init Live Search
- Step 13: Index your existing content
- Step 14: Verify it’s working
- Indexing additional post types
- A few operational notes
Before you start
- A VPS running Ubuntu 22.04 or 24.04, with SSH access (root or sudo)
- A WordPress site with Init Live Search 1.9.3 or later installed
- WP-CLI installed on the server running WordPress (check with
wp --info) - A subdomain pointed at your Meilisearch VPS, e.g.
search.example.com(only needed if Meilisearch and WordPress live on different servers)
Meilisearch is lightweight — for a blog with a few thousand posts, a 1-2GB RAM VPS is plenty, even when running alongside WordPress on the same machine.
Step 1: Prepare the system
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl ufw nginxStep 2: Create a dedicated user
Meilisearch shouldn’t run as root. Create a dedicated user and directories:
sudo useradd -r -s /bin/false meilisearch
sudo mkdir -p /var/lib/meilisearch/data /var/lib/meilisearch/dumps /etc/meilisearch
sudo chown -R meilisearch:meilisearch /var/lib/meilisearchStep 3: Download and install Meilisearch
curl -L https://install.meilisearch.com | sh
sudo mv ./meilisearch /usr/local/bin/
sudo chmod +x /usr/local/bin/meilisearch
meilisearch --versionStep 4: Generate a master key
The master key grants full administrative control over Meilisearch — only use it to generate scoped API keys in the next steps. Never put it into WordPress or anywhere outside this VPS.
openssl rand -base64 48Save this string somewhere safe — you’ll need it in the next step.
Step 5: Create the configuration file
sudo tee /etc/meilisearch/config.toml > /dev/null <<'EOF'
env = "production"
http_addr = "127.0.0.1:7700"
master_key = "PASTE_YOUR_MASTER_KEY_HERE"
db_path = "/var/lib/meilisearch/data"
dump_dir = "/var/lib/meilisearch/dumps"
no_analytics = true
max_indexing_memory = "384 MiB"
max_indexing_threads = 1
EOF
sudo chown meilisearch:meilisearch /etc/meilisearch/config.toml
sudo chmod 600 /etc/meilisearch/config.tomlhttp_addr = 127.0.0.1:7700 means Meilisearch only listens locally and isn’t exposed directly to the internet — much safer than binding to 0.0.0.0. If your VPS has limited RAM (under 4GB) or runs alongside WordPress, keep max_indexing_memory conservative to avoid resource contention during bulk reindexing.
Step 6: Create a systemd service
sudo tee /etc/systemd/system/meilisearch.service > /dev/null <<'EOF'
[Unit]
Description=Meilisearch
After=network.target
[Service]
Type=simple
User=meilisearch
Group=meilisearch
ExecStart=/usr/local/bin/meilisearch --config-file-path /etc/meilisearch/config.toml
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
MemoryMax=512M
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now meilisearch
sudo systemctl status meilisearchMemoryMax=512M acts as an OS-level safety net: if Meilisearch exceeds this limit, systemd kills and restarts it directly, rather than letting the kernel’s OOM killer pick a process at random — which could otherwise take down MySQL or PHP-FPM on a shared server.
Step 7: Verify Meilisearch is running
curl -s http://127.0.0.1:7700/healthExpected output: {"status":"available"}
Step 8: Configure the firewall
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw deny 7700
sudo ufw enableStep 9: Expose it to the internet via Nginx (only if WordPress is on a different server)
Point your subdomain’s DNS (e.g. search.example.com) at this VPS’s IP first, then:
sudo tee /etc/nginx/sites-available/search.example.com > /dev/null <<'EOF'
server {
listen 80;
server_name search.example.com;
location / {
proxy_pass http://127.0.0.1:7700;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/search.example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d search.example.comIf Meilisearch runs on the same server as WordPress, you can set up a subdomain the same way, or skip the Nginx step entirely and have Init Live Search call http://127.0.0.1:7700 directly.
Step 10: Create the index
curl -X POST 'http://127.0.0.1:7700/indexes' \
-H 'Authorization: Bearer MASTER_KEY' \
-H 'Content-Type: application/json' \
--data '{ "uid": "my_site_posts", "primaryKey": "id" }'Configure the index’s search attributes:
curl -X PATCH 'http://127.0.0.1:7700/indexes/my_site_posts/settings' \
-H 'Authorization: Bearer MASTER_KEY' \
-H 'Content-Type: application/json' \
--data '{
"searchableAttributes": ["title", "excerpt", "content"],
"filterableAttributes": ["post_type", "categories"],
"sortableAttributes": ["date_timestamp"],
"displayedAttributes": ["id", "title", "excerpt", "url", "thumbnail", "date"],
"rankingRules": ["words", "typo", "proximity", "attribute", "sort", "exactness"]
}'Step 11: Create scoped API keys
Never use the master key inside WordPress. Create two keys with limited permissions instead:
curl -X POST 'http://127.0.0.1:7700/keys' \
-H 'Authorization: Bearer MASTER_KEY' \
-H 'Content-Type: application/json' \
--data '{
"description": "Indexing key",
"actions": ["documents.add", "documents.delete", "indexes.get"],
"indexes": ["my_site_posts"],
"expiresAt": null
}'
curl -X POST 'http://127.0.0.1:7700/keys' \
-H 'Authorization: Bearer MASTER_KEY' \
-H 'Content-Type: application/json' \
--data '{
"description": "Search key",
"actions": ["search"],
"indexes": ["my_site_posts"],
"expiresAt": null
}'Each command returns a key string in its JSON response — save both, and label them clearly: the Search Key (search-only, safe to use on the frontend) and the Admin/Indexing Key (has write/delete permissions, keep it private).
Step 12: Configure Init Live Search
In your WordPress admin, go to Settings → Init Live Search → Meilisearch and fill in:
- Enable Meilisearch: check this box to turn it on
- Host URL: your Meilisearch address, e.g.
https://search.example.com(orhttp://127.0.0.1:7700if it’s on the same server) - Index Name: the index you created in Step 10, e.g.
my_site_posts - Search API Key: paste the Search Key from Step 11
- Admin / Indexing Key: paste the Admin/Indexing Key — or leave this blank if you’re defining it via a wp-config.php constant (recommended, more secure)
- Request Timeout: the default 3000ms works well for most setups
For better security on the Admin/Indexing Key, add this to wp-config.php instead of entering it directly in the settings field:
define('INIT_LIVE_SEARCH_MEILI_ADMIN_KEY', 'your-admin-key');Save your changes, then click Test Connection to confirm everything is wired up correctly.
Step 13: Index your existing content
Run this over SSH to push all published posts into Meilisearch:
wp init-live-search meili-reindex --path=/path/to/wordpressIf WordPress runs under a specific user (not root), run it as that user, for example:
sudo -u www-data -- wp init-live-search meili-reindex --path=/path/to/wordpressThe command processes posts in batches of 200, logging progress as it goes. For larger sites, adjust the batch size with the --batch-size flag.
Step 14: Verify it’s working
Try a search directly on your site — results are now served by Meilisearch. To confirm via the API directly:
curl -X POST 'https://search.example.com/indexes/my_site_posts/search' \
-H 'Authorization: Bearer SEARCH_KEY' \
-H 'Content-Type: application/json' \
--data '{"q": "test query"}'Indexing additional post types
To index post types not shown in the “Post Types to Include” checkboxes (for example, a custom post type that isn’t registered as public), add this to your theme’s functions.php:
add_filter('init_plugin_suite_live_search_post_types', function ($post_types) {
$post_types[] = 'your_post_type';
return array_unique($post_types);
});Then run the reindex command from Step 13 again to bring in the existing content for that post type.
A few operational notes
- If Meilisearch is disabled or has an outage, Init Live Search automatically falls back to the database search — no intervention needed.
- New, updated, and deleted posts sync to Meilisearch automatically once the integration is enabled — you only need to run the reindex command again if you’re rebuilding the entire index.
- If the Test Connection button reports an HTTP 403 error, double-check the Search Key for typos, stray whitespace, or an accidentally swapped key.

Comments