Databases Azure

Qdrant on Ubuntu 24.04 on Azure User Guide

| Product: Qdrant on Ubuntu 24.04 LTS on Azure

Overview

Qdrant is a high performance open source vector similarity search engine and vector database, written in Rust for speed and reliability under heavy load. It stores points made of a vector and a JSON payload, indexes them with HNSW, and answers nearest neighbour, filtered and hybrid search queries in milliseconds through a clean REST and gRPC API. It ships a built in web dashboard for browsing collections, running interactive API requests and exploring search results, and it is a strong foundation for semantic search, recommendations, retrieval augmented generation and anomaly detection.

The cloudimg image installs the official prebuilt Qdrant server binary at /opt/qdrant/bin/qdrant, running as the qdrant service bound to loopback 127.0.0.1:6333 (REST and dashboard) and 127.0.0.1:6335 (gRPC), behind nginx which terminates TLS. nginx serves the REST API and dashboard on port 443 and reverse proxies gRPC over TLS on port 6334, so the API key never crosses the wire in plaintext.

Secure by default, no open API: Qdrant serves an open, unauthenticated API when no key is set. This image never ships that. A qdrant-firstboot.service oneshot generates a unique API key on each VM's first boot, writes it to a root only file and the service environment, regenerates a per VM TLS certificate, resolves the instance URL and writes a root only info note, then disables itself. The qdrant service requires the first boot unit, so it can never start open before a key is set. No two VMs share a key and none is baked into the image.

Dedicated data disk: vector data (the storage and snapshots directories) lives on a dedicated data disk mounted at /var/lib/qdrant, captured into the image so every VM is provisioned with it. The server binary and dashboard assets stay on the OS disk.

What is included:

  • Qdrant (verified at 1.18.3) as the official prebuilt Rust server binary
  • The built in Qdrant web dashboard, served over TLS
  • qdrant.service bound to loopback 127.0.0.1:6333 (REST + dashboard) and 127.0.0.1:6335 (gRPC)
  • nginx terminating TLS: REST + dashboard on 443, gRPC on 6334, 80 redirects to HTTPS
  • qdrant-firstboot.service for the first boot API key, per VM TLS certificate and info note
  • A unique per VM API key generated on first boot, in a root only 0600 file
  • A dedicated data disk mounted at /var/lib/qdrant for storage and snapshots
  • 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. Qdrant performance and index capacity scale with memory. Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM) for evaluation and small collections, or a Standard_D4s_v5 or larger for production workloads with millions of vectors. The dedicated data disk holds your collections, so resize it to suit your dataset.

Step 1: Deploy from the Azure Portal

Search the Marketplace for Qdrant on Ubuntu 24.04, choose your VM size, and attach an NSG that allows TCP 22 (SSH) from your management network and TCP 443 (REST + dashboard) and 6334 (gRPC) from the networks and applications that need to query it. Port 80 only issues an HTTPS redirect.

Step 2: Deploy from the Azure CLI

RG="qdrant-prod"; LOCATION="eastus"; VM_NAME="qdrant-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/qdrant-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 qdrant-vnet --address-prefix 10.90.0.0/16 --subnet-name qdrant-subnet --subnet-prefix 10.90.1.0/24
az network nsg create -g "$RG" --name qdrant-nsg
az network nsg rule create -g "$RG" --nsg-name qdrant-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 qdrant-nsg --name allow-api --priority 110 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 443 6334 --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 qdrant-vnet --subnet qdrant-subnet --nsg qdrant-nsg --public-ip-sku Standard

Step 3: Connect via SSH

ssh azureuser@<vm-ip>

Step 4: Verify the services are running

Qdrant listens on loopback 127.0.0.1:6333 (REST + dashboard) and 127.0.0.1:6335 (gRPC); nginx fronts them with TLS on ports 443 and 6334. The health endpoint is public; the data API requires your key.

sudo systemctl is-active qdrant nginx
ss -tln | grep -E '127.0.0.1:6333|:443 '
curl -ks https://127.0.0.1/healthz
cat /opt/qdrant/VERSION

You should see both services active, the listening sockets, healthz check passed, and the installed Qdrant version.

Terminal showing the qdrant and nginx services active, the qdrant service status with its config path, and the installed Qdrant 1.18.3 version

The built in dashboard is served over TLS at https://<your-vm-ip>/dashboard. Because the certificate is self signed per VM, your browser will warn on first visit; accept the exception (or trust /etc/nginx/tls/qdrant.crt) to reach it.

The Qdrant web dashboard Welcome screen served over TLS, showing the version and the Console, Collections, Tutorial and Datasets navigation

Step 5: Read your per VM API key and confirm auth is enforced

The API 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 data request. Confirm that unauthenticated and wrong key requests are rejected while the correct key is accepted.

sudo cat /root/qdrant-info.txt
echo "no key       -> HTTP $(curl -ks -o /dev/null -w '%{http_code}' https://127.0.0.1/collections)"
echo "wrong key    -> HTTP $(curl -ks -o /dev/null -w '%{http_code}' -H 'api-key: wrong' https://127.0.0.1/collections)"
QDRANT_KEY=$(sudo grep '^QDRANT_API_KEY=' /root/qdrant-info.txt | cut -d= -f2-)
echo "correct key  -> HTTP $(curl -ks -o /dev/null -w '%{http_code}' -H "api-key: $QDRANT_KEY" https://127.0.0.1/collections)"

The info note holds QDRANT_API_KEY and the instance URLs, is owned root:root with mode 0600. Unauthenticated and wrong key calls return 401; only the per VM key returns 200.

Terminal showing the API key enforced over TLS: no key returns HTTP 401, a wrong key returns 401, the correct key returns 200, and the nginx TLS response headers

Step 6: Create a collection and run your first vector search

Load the API key into a shell variable, create a movies collection with 4 dimensional cosine vectors, upsert a few points with payloads, then run a nearest neighbour search. Use wait=true so the upsert is indexed before you search.

QDRANT_KEY=$(sudo grep '^QDRANT_API_KEY=' /root/qdrant-info.txt | cut -d= -f2-)
H="api-key: $QDRANT_KEY"
curl -ks -X PUT https://127.0.0.1/collections/movies -H "$H" -H 'Content-Type: application/json' \
  --data '{"vectors":{"size":4,"distance":"Cosine"}}' | jq -r '.status'
curl -ks -X PUT 'https://127.0.0.1/collections/movies/points?wait=true' -H "$H" -H 'Content-Type: application/json' \
  --data '{"points":[
    {"id":1,"vector":[0.9,0.1,0.1,0.1],"payload":{"title":"Interstellar","genre":"scifi"}},
    {"id":2,"vector":[0.1,0.9,0.1,0.1],"payload":{"title":"Inception","genre":"scifi"}},
    {"id":3,"vector":[0.1,0.1,0.9,0.1],"payload":{"title":"The Matrix","genre":"scifi"}},
    {"id":4,"vector":[0.1,0.1,0.1,0.9],"payload":{"title":"Dune","genre":"scifi"}}
  ]}' | jq -r '.status'
curl -ks -X POST https://127.0.0.1/collections/movies/points/search -H "$H" -H 'Content-Type: application/json' \
  --data '{"vector":[0.85,0.15,0.1,0.1],"limit":3,"with_payload":true}' \
  | jq -r '.result[] | "\(.payload.title)  score=\(.score)"'

The search returns the nearest points to your query vector, each with its cosine similarity score and payload, in a fraction of a millisecond.

Terminal showing a REST vector round trip: create a collection, upsert a point, and a points search returning the matched point with a cosine score of 1.0 and its payload

You can see every collection and its status in the dashboard.

The Qdrant dashboard Collections view listing the collections with GREEN status, point counts, and their size and Cosine vector configuration

Step 7: Filter by payload

Add a payload index so you can filter searches by metadata, then combine vector similarity with a payload filter. This returns the nearest scifi titles only.

QDRANT_KEY=$(sudo grep '^QDRANT_API_KEY=' /root/qdrant-info.txt | cut -d= -f2-)
H="api-key: $QDRANT_KEY"
curl -ks -X PUT https://127.0.0.1/collections/movies/index -H "$H" -H 'Content-Type: application/json' \
  --data '{"field_name":"genre","field_schema":"keyword"}' | jq -r '.status'
curl -ks -X POST https://127.0.0.1/collections/movies/points/search -H "$H" -H 'Content-Type: application/json' \
  --data '{"vector":[0.2,0.1,0.9,0.7],"filter":{"must":[{"key":"genre","match":{"value":"scifi"}}]},"limit":4,"with_payload":true}' \
  | jq -r '.result[] | "\(.payload.title)  score=\(.score)"'

The same interactive requests can be run from the dashboard's built in Console, which shows the request and the live scored results side by side.

The Qdrant dashboard Console running a points search request against a collection, with the scored nearest neighbour results and their payloads shown alongside

Step 8: Explore collections in the dashboard

Open a collection in the dashboard to browse its points, inspect payloads and vectors, run "find similar" from any point, and visualise the vector space. This is the fastest way to sanity check your data.

The Qdrant dashboard collection detail view showing a point with its JSON payload, vector length, and the Points, Info, Snapshots, Visualize and Graph tabs

Step 9: Snapshots for backup and restore

Qdrant can snapshot a collection to the snapshots directory on the data disk, which you can download and restore elsewhere.

QDRANT_KEY=$(sudo grep '^QDRANT_API_KEY=' /root/qdrant-info.txt | cut -d= -f2-)
H="api-key: $QDRANT_KEY"
curl -ks -X POST https://127.0.0.1/collections/movies/snapshots -H "$H" | jq -r '.result.name'
curl -ks https://127.0.0.1/collections/movies/snapshots -H "$H" | jq -r '.result[].name'

Snapshots are written under /var/lib/qdrant/snapshots on the dedicated data disk.

Step 10: Enable a real TLS certificate

The appliance ships with a self signed certificate generated per VM. For production, point a DNS record at the VM and replace it with a CA signed certificate. With certbot you can obtain one and drop it into the nginx TLS paths.

sudo apt-get update && sudo apt-get install -y certbot
sudo certbot certonly --standalone -d your-domain.example.com
sudo cp /etc/letsencrypt/live/your-domain.example.com/fullchain.pem /etc/nginx/tls/qdrant.crt
sudo cp /etc/letsencrypt/live/your-domain.example.com/privkey.pem  /etc/nginx/tls/qdrant.key
sudo systemctl reload nginx

Clients can then verify the certificate normally instead of using -k.

Persistence, first boot and updates

Vector data lives on the dedicated data disk mounted at /var/lib/qdrant (the storage and snapshots directories) and is captured into the image, so a VM launched from this image is ready immediately. The first boot service generates the per VM API key, regenerates the per VM TLS certificate and writes a root only info note, then disables itself so subsequent reboots are unaffected. The OS ships fully patched with unattended security upgrades enabled.

Terminal showing the per VM instance info note with the dashboard URL and API key, the public version JSON from the root endpoint, and the login banner pointing users at the dashboard

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. Qdrant 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.