Qdrant
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 REST and gRPC API.
Qdrant ships a built-in web dashboard for browsing collections, running interactive API requests and exploring search results. It is a strong foundation for semantic search, recommendations, retrieval-augmented generation and anomaly detection.
The SecureAnyCloud image installs the official prebuilt Qdrant server binary at /opt/qdrant/bin/qdrant. It runs 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 and writes it to a root-only file and the service environment. It also regenerates a per-VM TLS certificate, resolves the instance URL, writes a root-only info note, and 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 you get:
- Qdrant (verified at 1.18.3) as the official prebuilt Rust server binary
- The built-in Qdrant web dashboard, served over TLS
qdrant.servicebound to loopback127.0.0.1:6333(REST + dashboard) and127.0.0.1:6335(gRPC)- nginx terminating TLS: REST + dashboard on
443, gRPC on6334, and80redirecting to HTTPS qdrant-firstboot.servicefor 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
0600file - A dedicated data disk mounted at
/var/lib/qdrantfor storage and snapshots - Ubuntu 24.04 LTS base, patched at build time
- 24/7 SecureAnyCloud support, 24h response SLA
Prerequisites
Before you deploy this image, you need an active Azure subscription, an SSH key pair, and a VNet with a subnet.
Qdrant performance and index capacity scale with memory. The recommended VM size is Standard_B2s (2 vCPU, 4 GB RAM) for evaluation and small collections, or 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.
Deploy from the Azure Portal
Search the Marketplace for Qdrant on Ubuntu 24.04 and choose your VM size. Attach an NSG that allows TCP 22 (SSH) from your management network. The NSG must also allow TCP 443 (REST + dashboard) and 6334 (gRPC) from the networks and applications that need to query Qdrant. Port 80 only issues an HTTPS redirect.
Deploy from the Azure CLI
RG="qdrant-prod"; LOCATION="eastus"; VM_NAME="qdrant-01" IMAGE_URN="sac1647283583153:qdrant-ubuntu-24-04:default:latest" 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
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:qdrant-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
--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 StandardConnect via SSH
ssh azureuser@<vm-ip>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/VERSIONYou should see both services active, the listening sockets, healthz check passed, and the installed Qdrant version.
The built-in dashboard is served over TLS at https://<your-vm-ip>/dashboard. The certificate is self-signed per VM, so your browser warns on the first visit. Accept the exception, or trust /etc/nginx/tls/qdrant.crt, to reach the dashboard.
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, because 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. It is owned by root:root with mode 0600. Unauthenticated and wrong-key calls return 401. Only the per-VM key returns 200.
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, and 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.
The dashboard shows every collection and its status.
Filter by payload
Add a payload index so you can filter searches by metadata, and then combine vector similarity with a payload filter. This search 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)"'You can run the same requests from the dashboard's built-in Console, which shows the request and the live scored results side by side.
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 check your data.
Snapshots for backup and restore
Qdrant can snapshot a collection to the snapshots directory on the data disk. You can download the snapshot and restore it 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.
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. You can obtain one with certbot and place it in 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 nginxClients 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. It then disables itself, so subsequent reboots are unaffected. The OS is patched at build time and ships with unattended security upgrades enabled.
Support
Every SecureAnyCloud deployment includes 24/7 support with a 24 hour response SLA. Contact sales@secureanycloud.com for help with deployment, configuration or scaling.
This is a repackaged open-source software product with additional charges for SecureAnyCloud 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.
Deploy on Azure
Launch Qdrant on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
This image runs Qdrant, the open-source vector similarity search engine, fully installed and configured from the official Qdrant Debian package. Qdrant runs as the qdrant.service systemd unit in single-node mode. The REST API is fronted on port 80 by an nginx reverse proxy that enforces an api-key header guard.
The same nginx server also listens on the canonical Qdrant REST port 6333, so existing Qdrant clients work without configuration changes. The gRPC port 6334 stays bound to 127.0.0.1 for security and is reached from a workstation over an SSH tunnel.
On the first boot of every deployed instance, a one-shot service generates a fresh Qdrant API key and writes it into /etc/qdrant/qdrant.env (consumed by qdrant.service via an EnvironmentFile drop-in). It restarts Qdrant so the new key is applied, and stores the plain-text value in /root/qdrant-credentials.txt (mode 0600, root only). No shared or default credentials ship in the image.
What you get:
- Qdrant installed from the official Qdrant Debian package, running as qdrant.service
- An nginx reverse proxy on ports 80 and 6333 with an api-key header guard
- gRPC on port 6334, bound to 127.0.0.1 and reached over an SSH tunnel
- A separate, independently resizable EBS volume mounted at
/var/lib/qdrantfor vector storage, collection segments and snapshots, so the database tier stays off the operating system disk - A unique API key generated on first boot of every instance
Prerequisites
Before you deploy this image, you need:
- An Amazon Web Services account where you can launch EC2 instances
- IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
- An EC2 key pair in the target Region for SSH access to the instance
- A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network, plus inbound port 80 (and optionally 6333) from the networks that will call the Qdrant API
- The AWS CLI version 2 installed locally, if you plan to deploy from the command line
Launch the Instance from the AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for Qdrant. Select the SecureAnyCloud listing and choose Select, then choose Continue on the subscription summary.
Pick an instance type of m5.large or larger as a balanced default, and size the instance to the vector workload you intend to host. Choose your EC2 key pair under Key pair (login). Under Network settings, select your VPC and subnet.
Create or select a security group that allows inbound port 22 from your management network and inbound port 80 (and optionally 6333) from the networks that will call the Qdrant API. Leave the root volume at the default size or larger.
Select Launch instance. First-boot initialisation takes only a few seconds after the instance state becomes Running and the status checks pass.
Launch the Instance from the AWS CLI
The following command launches an instance from the SecureAnyCloud Qdrant Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing, <key-name> with your EC2 key pair name, and <subnet-id> with your subnet ID. Replace <security-group-id> with a security group that opens ports 22 and 80 (and optionally 6333) from the networks that need them.
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name <key-name> \
--subnet-id <subnet-id> \
--security-group-ids <security-group-id> \
--block-device-mappings '{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":30,"VolumeType":"gp3"}}' \
--metadata-options 'HttpTokens=required,HttpEndpoint=enabled' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=qdrant}]'The image attaches a dedicated 30 GiB Qdrant data volume automatically, mounted at /var/lib/qdrant. To start with more vector storage, enlarge that volume on the Storage step in the console, or add a second block device mapping in the CLI.
Connect to the Instance over SSH
Connect to the instance with SSH as the default login user for the operating system variant you launched. The login user differs by variant:
- Ubuntu 24.04: the SSH login user is ubuntu
Replace <key-file> with the path to your private key file and <instance-public-ip> with the public IP address or DNS name of the instance.
ssh -i <key-file> ubuntu@<instance-public-ip>Retrieve the Per Instance API Key
On the very first boot, qdrant-firstboot.service generates an API key unique to this instance and writes it into /etc/qdrant/qdrant.env. It restarts Qdrant so the new key is applied, and stores the plain-text value in /root/qdrant-credentials.txt. The file is mode 0600 and readable by root only, so retrieve it with sudo:
sudo cat /root/qdrant-credentials.txtThe file lists the REST URLs for this instance, the api-key value, and ready-to-run curl examples for listing collections and creating a collection. Store the key in your secrets manager and remove the file from the instance once you have copied it.
The same key is also held in /etc/qdrant/qdrant.env and is loaded by qdrant.service on every restart, so the key survives instance reboots. To rotate the API key, edit /etc/qdrant/qdrant.env, replace the QDRANT__SERVICE__API_KEY value, and then run sudo systemctl restart qdrant.service.
Confirm the Service and Probe Health
The Qdrant daemon and the nginx reverse proxy run as systemd services and start automatically at boot. Confirm that both are active:
systemctl is-active qdrant.service nginx.serviceThe unauthenticated liveness probe answers from nginx on port 80 (and on port 6333), so you can wire it straight into an AWS target group health check or external monitoring:
curl http://127.0.0.1/healthzThe expected output is healthz check passed.
Confirm that the api-key header guard rejects requests that arrive without the header. The expected status is 401:
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1/collectionsThe api-key header guard runs at the nginx edge, so it applies whether your client connects to the REST API on port 80 or on port 6333.
Create a Collection and Upsert Vectors
Each recipe below reads the api-key from /root/qdrant-credentials.txt at the top of its block, so it works in a fresh shell session and in any automation that runs each block independently. Each block first sets the local KEY variable, and then uses $KEY in the api-key header.
Create a collection named my-vectors that holds 4-dimensional vectors compared with cosine distance:
KEY=$(sudo awk -F= '/^qdrant.api.key=/{print $2}' /root/qdrant-credentials.txt) && \
curl -X PUT -H "api-key: $KEY" -H "Content-Type: application/json" \
http://127.0.0.1/collections/my-vectors \
-d '{"vectors":{"size":4,"distance":"Cosine"}}'The expected response is {"result":true,"status":"ok","time":0.090188366} (the time value will differ).
Upsert two vectors with payloads. The wait=true query parameter blocks until the write is committed:
KEY=$(sudo awk -F= '/^qdrant.api.key=/{print $2}' /root/qdrant-credentials.txt) && \
curl -X PUT -H "api-key: $KEY" -H "Content-Type: application/json" \
"http://127.0.0.1/collections/my-vectors/points?wait=true" \
-d '{"points":[{"id":1,"vector":[0.1,0.2,0.3,0.4],"payload":{"city":"London"}},{"id":2,"vector":[0.9,0.8,0.7,0.6],"payload":{"city":"Paris"}}]}'The expected response is {"result":{"operation_id":1,"status":"completed"},"status":"ok","time":0.001748212}.
Run a Nearest Neighbour Search
Query the collection with a 4-dimensional vector and ask for the two nearest neighbours, including their payloads:
KEY=$(sudo awk -F= '/^qdrant.api.key=/{print $2}' /root/qdrant-credentials.txt) && \
curl -X POST -H "api-key: $KEY" -H "Content-Type: application/json" \
http://127.0.0.1/collections/my-vectors/points/search \
-d '{"vector":[0.1,0.2,0.3,0.4],"limit":2,"with_payload":true}'The first result has a score of 1.0 (an exact cosine match) and the payload {"city":"London"}. The second result has a lower score and the payload {"city":"Paris"}. The exact response on this instance was:
{"result":[{"id":1,"version":1,"score":1.0,"payload":{"city":"London"}},{"id":2,"version":1,"score":0.842701,"payload":{"city":"Paris"}}],"status":"ok","time":0.0005281}List all collections to see the new one:
KEY=$(sudo awk -F= '/^qdrant.api.key=/{print $2}' /root/qdrant-credentials.txt) && \
curl -H "api-key: $KEY" http://127.0.0.1/collectionsThe expected response is {"result":{"collections":[{"name":"my-vectors"}]},"status":"ok","time":4.1e-6}.
Reach gRPC over an SSH Tunnel
Qdrant's gRPC port 6334 stays bound to 127.0.0.1 on the instance for security. To use gRPC from your workstation, open an SSH tunnel that forwards local port 6334 to the instance's loopback gRPC port:
ssh -i <key-file> -L 6334:127.0.0.1:6334 ubuntu@<instance-public-ip>While the tunnel is up, point your gRPC client at localhost:6334 and pass the same api-key value in the metadata. For example, with the official Qdrant Python client:
from qdrant_client import QdrantClient client = QdrantClient(host="localhost", grpc_port=6334, prefer_grpc=True, api_key="<your-api-key>") print(client.get_collections())To expose gRPC publicly, open inbound port 6334 in the instance's security group and edit /etc/qdrant/config.yaml to set service.host to 0.0.0.0. Add TLS termination at nginx or another reverse proxy before you expose gRPC to the open internet.
Dedicated Data Volume
Vector segments, collection metadata and snapshots are stored under /var/lib/qdrant, which is a separate filesystem on its own EBS volume. Confirm the dedicated mount and review free space:
findmnt /var/lib/qdrantThe expected output reports the volume mounted as ext4:
TARGET SOURCE FSTYPE OPTIONS /var/lib/qdrant /dev/nvme1n1 ext4 rw,relatimedf -h /var/lib/qdrantBecause the data volume is independent, you can grow it without disturbing the operating system disk. Modify the EBS volume in the AWS console or with the CLI, and then extend the filesystem on the instance with sudo resize2fs <device>.
Maintenance and Tuning
Keep the operating system patched with the standard package manager. To upgrade Qdrant itself, download the next qdrant_<version>-1_amd64.deb from the official github.com/qdrant/qdrant releases page and run sudo apt-get install ./qdrant_<version>-1_amd64.deb.
The SecureAnyCloud overrides in /etc/qdrant/config.yaml, the systemd unit at /etc/systemd/system/qdrant.service, the EnvironmentFile drop-in and the nginx site config survive the upgrade.
The Qdrant configuration is in /etc/qdrant/config.yaml. Restart the service after you edit it:
sudo systemctl restart qdrant.serviceTo take a snapshot of a collection through the REST API:
KEY=$(sudo awk -F= '/^qdrant.api.key=/{print $2}' /root/qdrant-credentials.txt) && \
curl -X POST -H "api-key: $KEY" \
http://127.0.0.1/collections/my-vectors/snapshotsSnapshots are written under /var/lib/qdrant/snapshots and are picked up by your existing EBS snapshot policy on the data volume.
To delete a collection when you have finished experimenting:
KEY=$(sudo awk -F= '/^qdrant.api.key=/{print $2}' /root/qdrant-credentials.txt) && \
curl -X DELETE -H "api-key: $KEY" \
http://127.0.0.1/collections/my-vectorsScreenshots
Querying the Qdrant REST API with the api-key header to list collections on a freshly launched instance.
Upserting a vector point with its payload into a Qdrant collection through the REST API.
Running a nearest neighbour search query against a Qdrant collection and inspecting the results.
Support
This Amazon Machine Image is provided by SecureAnyCloud with 24/7 technical support by email and chat. Contact SecureAnyCloud for help with vector database deployment, collection design, indexing-parameter tuning, replication and performance optimisation.
Qdrant is a trademark of Qdrant Solutions GmbH. All other 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.
Deploy on AWS
Launch Qdrant on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com