Meilisearch on Ubuntu 24.04 on Azure User Guide
Overview
Meilisearch is a fast, open source search engine that returns relevant, typo tolerant results in milliseconds, with built in AI and vector search for hybrid semantic queries. It exposes a clean REST API with official client libraries for most languages, and adds filtering, faceting, custom ranking, synonyms and multi language tokenization out of the box.
The cloudimg image installs the official Meilisearch single binary at /usr/local/bin/meilisearch, running as the meilisearch service bound to loopback 127.0.0.1:7700, behind nginx on port 80. nginx is ready for your TLS certificate.
Secure by default, no default login: Meilisearch runs an insecure no authentication development mode when no master key is set. This image never ships that. The service runs in production mode, which refuses to start without a master key, so every API endpoint requires authentication. A meilisearch-firstboot.service oneshot generates a unique master key on each VM's first boot, writes it to a root only file, resolves the instance URL and writes a root only info note, then disables itself. No two VMs share a key and none is baked into the image.
Always current: on first boot the appliance downloads the newest Meilisearch release binary (falling back to the baked version if the network is unavailable), so every launch runs current software rather than the version frozen at build time.
Note on the interface: Meilisearch is an API first service. Version 1.49 ships no bundled web interface (a browser request to / returns a JSON status), so you drive it through its REST API, the official client libraries, or your own application. This guide uses curl against the API.
What is included:
- Meilisearch (verified at 1.49.0) as the official single static binary
meilisearch.servicein production mode, bound to127.0.0.1:7700- nginx reverse proxy on port 80, ready for TLS
meilisearch-firstboot.servicefor the first boot master key, binary refresh and info note- A unique per VM master key generated on first boot, in a root only
0600file - Ubuntu 24.04 LTS base, fully patched
- 24/7 cloudimg support, 24h response SLA
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet with a subnet. Meilisearch is lightweight, but search performance and index capacity scale with memory. Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM) for evaluation and small indexes, or a Standard_D2s_v5 or larger for production workloads with bigger datasets. Meilisearch keeps its index on the OS disk, so expand the disk before loading large collections.
Step 1: Deploy from the Azure Portal
Search the Marketplace for Meilisearch on Ubuntu 24.04, choose your VM size, and attach an NSG that allows TCP 22 (SSH) from your management network and TCP 80 / 443 (the API) from the networks and applications that need to query it. Front port 80 with TLS in production (see the HTTPS section below).
Step 2: Deploy from the Azure CLI
RG="meilisearch-prod"; LOCATION="eastus"; VM_NAME="meilisearch-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/meilisearch-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name meili-vnet --address-prefix 10.90.0.0/16 --subnet-name meili-subnet --subnet-prefix 10.90.1.0/24
az network nsg create -g "$RG" --name meili-nsg
az network nsg rule create -g "$RG" --nsg-name meili-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name meili-nsg --name allow-web --priority 110 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 80 443 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name meili-vnet --subnet meili-subnet --nsg meili-nsg --public-ip-sku Standard
Step 3: Connect via SSH
ssh azureuser@<vm-ip>
Step 4: Verify the services are running
Meilisearch listens on loopback 127.0.0.1:7700 and nginx fronts the API on port 80. The health endpoint is public; everything else requires your master key.
sudo systemctl start meilisearch nginx 2>/dev/null || true
sudo systemctl is-active meilisearch nginx
ss -tln | grep -E ':80 |127.0.0.1:7700'
curl -s http://127.0.0.1/health
You should see both services active, the two listening sockets, and {"status":"available"} from the health endpoint.

Step 5: Read your per VM master key
The master key was generated on this VM's first boot and stored in a root only file. Read it and keep it secret: it authenticates every API request. Confirm that unauthenticated requests are rejected.
sudo cat /root/meilisearch-info.txt
echo "unauthenticated /keys -> HTTP $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/keys)"
The info note holds MEILI_MASTER_KEY and the instance URL, is owned root:root with mode 0600, and an unauthenticated call to /keys returns 401 because production mode enforces authentication.

Step 6: Create an index and run your first search
Load the master key into a shell variable, create a movies index, configure its attributes, add a few documents, then run a typo tolerant search. Indexing is asynchronous, so wait for the documents to be processed before searching.
export MEILI_KEY=$(sudo grep '^MEILI_MASTER_KEY=' /root/meilisearch-info.txt | cut -d= -f2-)
H="Authorization: Bearer $MEILI_KEY"
curl -s -X PATCH http://127.0.0.1/indexes/movies/settings -H "$H" -H 'Content-Type: application/json' \
--data '{"filterableAttributes":["genres","release_year"],"sortableAttributes":["release_year"]}' >/dev/null
curl -s -X POST 'http://127.0.0.1/indexes/movies/documents?primaryKey=id' -H "$H" -H 'Content-Type: application/json' \
--data '[
{"id":1,"title":"Interstellar","genres":["Science Fiction","Drama"],"release_year":2014},
{"id":2,"title":"Inception","genres":["Action","Science Fiction"],"release_year":2010},
{"id":3,"title":"The Matrix","genres":["Action","Science Fiction"],"release_year":1999},
{"id":4,"title":"Dune","genres":["Science Fiction","Adventure"],"release_year":2021},
{"id":5,"title":"Arrival","genres":["Science Fiction","Drama"],"release_year":2016}
]' >/dev/null
for i in $(seq 1 20); do
N=$(curl -s -H "$H" http://127.0.0.1/indexes/movies/stats | jq -r '.numberOfDocuments // 0')
[ "$N" -ge 5 ] && break; sleep 1
done
echo "indexed documents: $N"
curl -s -X POST http://127.0.0.1/indexes/movies/search -H "$H" -H 'Content-Type: application/json' \
--data '{"q":"scince fiction","limit":3}' | jq -r '.hits[].title'
Note the deliberate typo in the query (scince fiction): Meilisearch still returns the science fiction titles, ranked by relevance, in a fraction of a millisecond.

Step 7: Filter, sort and facet
Because you marked genres and release_year filterable and release_year sortable in Step 6, you can now filter and sort results. This returns science fiction films released after 2013, newest first.
export MEILI_KEY=$(sudo grep '^MEILI_MASTER_KEY=' /root/meilisearch-info.txt | cut -d= -f2-)
H="Authorization: Bearer $MEILI_KEY"
curl -s -X POST http://127.0.0.1/indexes/movies/search -H "$H" -H 'Content-Type: application/json' \
--data '{"q":"","filter":"genres = \"Science Fiction\" AND release_year > 2013","sort":["release_year:desc"],"limit":5,"attributesToRetrieve":["title","release_year"]}' \
| jq -r '.hits[] | .title + " (" + (.release_year|tostring) + ")"'
Step 8: AI powered hybrid search
Meilisearch combines classic keyword search with vector search so a query can match on meaning as well as words. Configure an embedder on the index (for example an OpenAI compatible provider, or supply your own vectors), then pass a hybrid object on the search request. The embedder configuration looks like this:
{
"embedders": {
"default": {
"source": "openAi",
"apiKey": "YOUR_PROVIDER_API_KEY",
"model": "text-embedding-3-small"
}
}
}
With an embedder set, add "hybrid": {"embedder": "default", "semanticRatio": 0.5} to any search body to blend keyword and semantic relevance. See the Meilisearch documentation for embedder options and self hosted models.
Step 9: Enable HTTPS
nginx fronts the API on port 80 and is ready for TLS. Point a DNS record at the VM, then obtain a certificate with certbot and let it configure nginx for port 443.
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com
After issuance, restrict the NSG so only 443 (and 22 from your management network) is reachable, and query the API over https://your-domain.example.com/.
Persistence, first boot and updates
The search index lives on the OS disk under /var/lib/meilisearch/data.ms and is captured into the image, so a VM launched from this image is ready immediately. The first boot service generates the per VM master key, refreshes the Meilisearch binary to the latest release, then disables itself so subsequent reboots are unaffected. The OS ships fully patched with unattended security upgrades enabled.

Support
Every cloudimg deployment includes 24/7 support with a 24 hour response SLA. Contact support@cloudimg.co.uk for help with deployment, configuration or scaling.
This is a repackaged open source software product with additional charges for cloudimg support services. Meilisearch is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.