//------------------------------------------------------------------- //-------------------------------------------------------------------
Build a Private Search API with Meilisearch

How to Install Meilisearch with Docker and Build a Private Search API on Ubuntu

If you want to build a private search API with Meilisearch, this guide provides a full setup on Ubuntu using Docker, with persistent storage, API keys, an example index, and a reverse proxy. With Meilisearch, you get full control over your data, zero usage limits, and a search engine that runs entirely on your own server.

What Is Meilisearch?

Meilisearch is an open-source, self-hosted search engine built for speed and simplicity. It returns results as you type, handles typos, and integrates easily with any app via its REST API.

Meilisearch is a great fit for:

  • E-commerce product search.
  • Documentation sites with a search bar.
  • SaaS apps that need internal content search.
  • Any self-hosted app where you do not want to pay for Algolia or Elasticsearch.

If you are looking for a reliable place to run Meilisearch, a flexible Linux VPS is a good option.

Prerequisites to Build a Private Search API with Meilisearch

Before you begin, make sure you have the following:

  • Ubuntu 22.04 or 24.04 server.
  • A non-root user with sudo access.
  • At least 1 vCPU, 1 GB RAM, and 10 GB disk space.
  • Docker and Docker Compose are installed on your server.
  • A domain name or IP address pointing to your server.

In this guide, we assume you have Docker and Docker Compose installed and ready. Proceed to the following steps to build a private search API with Meilisearch.

Step 1. Create the Project Directory and Generate a Strong Meilisearch Master Key

First, run the system update and make sure Docker is running:

apt update
docker run hello-world

If you see the Hello World message, Docker is ready.

Create a project directory to keep everything organized and switch to it:

mkdir -p ~/meilisearch && cd ~/meilisearch

The master key is the most important security setting in Meilisearch. Never run Meilisearch without it, especially in production. Use openssl to generate a random, secure key:

openssl rand -hex 32

Copy the output; you will need it for the next steps.

Important Note: The master key must be at least 16 bytes. A 32-byte hex string is a solid choice. Keep it secret, it gives full control over your Meilisearch instance.

Step 2. Create the Meilisearch Environment File

You can easily store secrets in a .env file instead of hardcoding them into your Docker Compose YAML file.

Create the file with your desired text editor:

nano .env

Paste the following variables and replace the value with your generated key:

MEILI_MASTER_KEY=your_generated_master_key_here
MEILI_ENV=production
MEILI_NO_ANALYTICS=true
  • MEILI_ENV=production enables authentication and disables the open search preview UI.
  • MEILI_NO_ANALYTICS=true stops Meilisearch from sending anonymous usage data.

Step 3. Write the Meilisearch Docker Compose File

At this point, you can define your service, volumes, and network. To build a private search API with Meilisearch cleanly, create the Docker Compose YAML file with:

nano docker-compose.yml

Paste this configuration into the file:

services:
  meilisearch:
    image: getmeili/meilisearch:v1.8
    container_name: meilisearch
    restart: unless-stopped
    ports:
      - "127.0.0.1:7700:7700"
    environment:
      - MEILI_MASTER_KEY=${MEILI_MASTER_KEY}
      - MEILI_ENV=${MEILI_ENV}
      - MEILI_NO_ANALYTICS=${MEILI_NO_ANALYTICS}
    volumes:
      - meili_data:/meili_data
    networks:
      - search_net
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--spider", "http://localhost:7700/health"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  meili_data:

networks:
  search_net:
    driver: bridge

Key things include:

  • 127.0.0.1:7700:7700: Binds the port to localhost only, so the API is not directly reachable from the internet. Traffic goes through nginx instead.
  • meili_data:/meili_data: Uses a Docker-managed named volume for persistent storage. Your indexes survive container restarts and updates.
  • restart: unless-stopped: The container restarts automatically on reboot or crash.
  • healthcheck: Docker will monitor the /health endpoint and show the container as unhealthy if Meilisearch is not responding.

Step 4. Start Meilisearch

Once you are done, use the command below to start Meilisearch:

docker compose up -d
Start Meilisearch

Then, use the commands below to check that it is running:

docker compose ps
docker compose logs meilisearch
Check Meilisearch docker container status

From the logs, you should see output confirming Meilisearch has started and a master key is set.

Also, test the health endpoint from the server itself:

curl http://localhost:7700/health

In the output, you must see:

{"status":"available"}

Step 5. Retrieve Your Meilisearch API Keys

When you start Meilisearch with a master key, it automatically generates default API keys. You must use these for the operations, never the master key directly.

To fetch them, use the command below with your master key:

curl -X GET 'http://localhost:7700/keys' \
  -H "Authorization: Bearer YOUR_MASTER_KEY"

You will get back four default keys:

Key NamePurposeUse From
Default Search API KeySearch queries onlyFrontend and client apps
Default Admin API KeyFull access, except key managementBackend and server only
Default Read-Only Admin API KeyRead-only accessMonitoring tools
Default Chat API KeyConversational searchFrontend

Rule of thumb: Use the Admin API Key when indexing data or changing settings. Use the Search API Key in your app’s frontend or public-facing search widget. Never expose the master key in code.

Step 6. Create a Products Index and Add Documents

At this point, you can build a private search API with Meilisearch by creating a real index with sample product data.

Use the command below to create a products index:

curl -X POST 'http://localhost:7700/indexes' \
  -H "Authorization: Bearer YOUR_ADMIN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "uid": "products",
    "primaryKey": "id"
  }'

Add documents to the index:

curl -X POST 'http://localhost:7700/indexes/products/documents' \
  -H "Authorization: Bearer YOUR_ADMIN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '[
    {
      "id": 1,
      "name": "Wireless Keyboard",
      "description": "Compact Bluetooth keyboard with backlight",
      "category": "Electronics",
      "price": 49.99
    },
    {
      "id": 2,
      "name": "Mechanical Mouse",
      "description": "Ergonomic gaming mouse with RGB lighting",
      "category": "Electronics",
      "price": 39.99
    },
    {
      "id": 3,
      "name": "USB-C Hub",
      "description": "7-in-1 hub with HDMI, USB 3.0, and SD card reader",
      "category": "Accessories",
      "price": 29.99
    }
  ]'

Meilisearch processes document additions asynchronously. It returns a task ID immediately. You can check the task status:

curl -X GET 'http://localhost:7700/tasks/0' \
  -H "Authorization: Bearer YOUR_ADMIN_API_KEY"

When the status shows “succeeded“, the documents are indexed and ready.

Then, run a search query with:

curl -X POST 'http://localhost:7700/indexes/products/search' \
  -H "Authorization: Bearer YOUR_SEARCH_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"q": "keyboard"}'

You will get back the matching products instantly. Even try a typo like “keybord“, Meilisearch handles it.

By default, Meilisearch searches all fields. In production, it is better to tell it which fields matter:

curl -X PUT 'http://localhost:7700/indexes/products/settings/searchable-attributes' \
  -H "Authorization: Bearer YOUR_ADMIN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '["name", "description", "category"]'

Also, you can set filterable attributes for faceted search:

curl -X PUT 'http://localhost:7700/indexes/products/settings/filterable-attributes' \
  -H "Authorization: Bearer YOUR_ADMIN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '["category", "price"]'

This allows queries like: search for “mouse” but only in the Electronics category.

Build a Private Search API with Meilisearch Behind Nginx

Exposing port 7700 directly to the internet is risky. The right way to build a private search API with Meilisearch for production use is to put it behind a reverse proxy.

Install Nginx with the command below:

apt install nginx -y

Create the Nginx config file for your domain:

nano /etc/nginx/sites-available/meilisearch

Paste this plain HTTP config with your actual domain:

server {
    listen 80;
    server_name your-domain.com;

    location / {
        proxy_pass http://127.0.0.1:7700;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Enable the site and test the config:

ln -s /etc/nginx/sites-available/meilisearch /etc/nginx/sites-enabled/
nginx -t

You should see:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reload Nginx to apply the changes:

sudo systemctl reload nginx

Make sure port 80 is open in your firewall:

sudo ufw allow 'Nginx Full'
sudo ufw allow OpenSSH
sudo ufw enable

Then, install Certbot and the Nginx plugin and run Certbot:

sudo certbot --nginx -d your-domain.com

Certbot will ask for your email address and prompt you to agree to the terms. It will then:

  • Verify that your domain points to the server.
  • Obtain a free Let’s Encrypt certificate.
  • Automatically update your Nginx config to add SSL.

Verify SSL is working:

curl https://your-domain.com/health

You should get back:

{"status":"available"}

Test automatic certificate renewal:

sudo certbot renew --dry-run

Certbot sets up a cron job or systemd timer to renew the certificate automatically before it expires. You do not need to do anything manually after this point.

Note: If you want to build a private search API with Meilisearch that stays private, block direct access to port 7700 from the outside world. The API should only be reachable through Nginx:

sudo ufw deny 7700
sudo ufw status

Keep Your Meilisearch Data Safe with Backups

Data in a Docker named volume (meili_data) survives container restarts. But for real backup protection, you should export dumps regularly.

Create a dump with the command below:

curl -X POST 'http://localhost:7700/dumps' \
  -H "Authorization: Bearer YOUR_ADMIN_API_KEY"

Meilisearch saves the dump inside the volume at /meili_data/dumps/. You can mount this path and copy dumps to external storage or an S3-compatible bucket on a schedule.

View available dumps:

docker exec meilisearch ls /meili_data/dumps/

How To Update Meilisearch

To update to a newer version, just change the image tag in your docker-compose.yml, then restart:

docker compose pull
docker compose up -d

Because you are using a named volume, your data is not lost during updates. Always read the Meilisearch release notes before updating in production; major versions sometimes require a dump migration.

For more information, you can visit the Meilisearch Official Documentation.

Conclusion

Now you have a fully working Meilisearch deployment on Ubuntu. This is how you build a private search API with Meilisearch the right way, not just on localhost for development, but as a real deployable service.

The setup is minimal enough to run on a single VPS. Once your data grows and query volume increases, the natural step is to move to a dedicated server hosting where Meilisearch gets its own dedicated resources and does not share CPU or RAM with other services.

We hope you enjoy this guide.

FAQs

Does Meilisearch need a lot of resources?

Not at all. It runs well on 1 vCPU and 1 GB RAM for small to medium indexes. Larger datasets need more RAM since Meilisearch loads indexes into memory.

Can I use Meilisearch without Docker?

Yes. You can install the binary directly or use a package manager. Docker is just the easiest way to deploy and manage it.

Can I use Meilisearch with any programming language?

Yes. Since it exposes a REST API, any language that can make HTTP requests works. Official SDKs exist for JavaScript, Python, PHP, Ruby, Go, and more.

Post Your Comment

PerLod delivers high-performance hosting with real-time support and unmatched reliability.

Contact us

Payment methods

payment gateway
Perlod Logo
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.