Qdrant 1.19.2 on AWS User Guide
Overview
This guide covers launching and using the Qdrant 1.19.2 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg Vector Databases and RAG listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
Qdrant is an open source vector similarity search engine and vector database written in Rust. It stores points (a vector plus a JSON payload), indexes them with HNSW and answers nearest neighbour, filtered and hybrid searches through a REST API and a gRPC API. It is a common foundation for semantic search, recommendations and retrieval augmented generation (RAG). Qdrant also ships a web dashboard for browsing collections and running requests.
Out of the box, Qdrant serves an open API that anyone who can reach it may read and write. This image never does that. The cloudimg image adds:
- Two keys unique to your instance, required on every data call. An API key (full access) and a read-only API key (list, read and search only) are generated on your instance's first boot, written to Qdrant's configuration and to
/root/qdrant-info.txt, readable by root only. They never appear on a command line, in a process environment or in a log. Qdrant refuses to start if either key is missing, so it can never come up open. - HTTPS and gRPC over TLS, with a certificate made on your instance. Qdrant listens on loopback only (
127.0.0.1:6333REST and127.0.0.1:6336gRPC). nginx serves the REST API and dashboard on 443 and the gRPC API on 6334, TLS 1.2 and 1.3 only, with a certificate and key generated on first boot. Port 80 only redirects to HTTPS (plus a/healthzcheck). - The host name comes from configuration, never from a request. nginx answers only to your instance's own names and addresses (public IP and DNS name, private IP and DNS name, or the name you set with
qdrant-set-host) and closes the connection for any otherHost. - The client address comes from the connection. nginx overwrites
X-Forwarded-ForandX-Real-IPwith the real peer address and dropsForwarded, so a client cannot pose as another address in the logs. - Instance metadata is blocked for Qdrant and nginx. Qdrant can fetch a snapshot from a URL you give it; it cannot reach the EC2 instance metadata service at 169.254.169.254. The instance requires IMDSv2.
- Telemetry off and a single node. Usage reporting to Qdrant is disabled, and clustering (with its internal peer API on 6335) is off.
- Exactly four public ports: 22 (SSH), 80 (redirect), 443 (REST and dashboard) and 6334 (gRPC).
What is on the image: Qdrant 1.19.2 (the official release binary and dashboard, checksum verified), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. Your vector data (collections and snapshots) lives on its own 30 GiB EBS volume at /var/lib/qdrant. The Apache 2.0 licence is at /usr/share/doc/cloudimg/qdrant/LICENSE.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet.
- A security group that allows TCP 443 (REST API and dashboard) and TCP 6334 (gRPC) from the applications and people that use Qdrant, TCP 22 (SSH) from your management network, and optionally TCP 80 (it only redirects). Keep these to your own networks rather than
0.0.0.0/0where you can.
Qdrant keeps its indexes in memory where it can, so size the instance for your data: m5.large (8 GiB) suits evaluation and up to a few million small vectors; choose a memory optimised type for larger collections, and grow the data volume as your collections grow.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Qdrant 1.19.2 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (or larger), your key pair and a security group as above. Keep the second EBS volume: it holds your collections.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=qdrant}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| Qdrant 1.19.2 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services, ports and data volume
First boot takes a few seconds. Then Qdrant, nginx and the two cloudimg first boot units are active:
systemctl is-active qdrant nginx qdrant-firstboot qdrant-set-host
active
active
active
active
Qdrant listens on loopback only; nginx listens on 80, 443 and 6334, and SSH on 22:
sudo ss -tlnp | awk 'NR==1 || /nginx|sshd|qdrant/' | awk '{print $1, $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g'
State Local Peer
LISTEN 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
LISTEN 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
LISTEN 0.0.0.0:22 users:(("sshd"),("systemd"))
LISTEN 0.0.0.0:6334 users:(("nginx"),("nginx"),("nginx"))
LISTEN 127.0.0.1:6333 users:(("qdrant"))
LISTEN 127.0.0.1:6336 users:(("qdrant"))
LISTEN [::]:443 users:(("nginx"),("nginx"),("nginx"))
LISTEN [::]:80 users:(("nginx"),("nginx"),("nginx"))
LISTEN [::]:22 users:(("sshd"),("systemd"))
LISTEN [::]:6334 users:(("nginx"),("nginx"),("nginx"))
The version, as reported by the binary and by the running server:
/opt/qdrant/bin/qdrant --version
curl -ks https://127.0.0.1/ | jq -c .
qdrant 1.19.2
{"title":"qdrant - vector search engine","version":"1.19.2","commit":"016542aa5deb6c66380bb137badf73d54f742bde"}
Your collections are on the separate data volume:
findmnt -no SOURCE,TARGET,FSTYPE /var/lib/qdrant
df -h --output=size,avail,target /var/lib/qdrant
/dev/nvme1n1 /var/lib/qdrant ext4
Size Avail Mounted on
30G 28G /var/lib/qdrant
Step 4: Get your API keys and URLs
sudo cat /root/qdrant-info.txt
# Qdrant 1.19.2 - generated on this instance. Unique to this instance: keep it secret.
# Send a key in the 'api-key' header (REST) or the 'api-key' metadata (gRPC). The read-only key can list,
# read and search but cannot create, change or delete anything (snapshots included).
QDRANT_API_KEY=<your-api-key>
QDRANT_READ_ONLY_API_KEY=<your-read-only-api-key>
QDRANT_URL=https://<public-ip>/
QDRANT_DASHBOARD_URL=https://<public-ip>/dashboard
QDRANT_GRPC_URL=<public-ip>:6334
# TLS certificate (self-signed, per instance): /etc/nginx/tls/qdrant.crt
# Serve under a DNS name: sudo qdrant-set-host <name> Rotate both keys: sudo qdrant-set-keys
# Guide: https://www.cloudimg.co.uk/guides/qdrant-1-19-2-on-ubuntu-24-04-aws/
Give applications that only search the read-only key, and keep the full API key for the applications and people that load and manage data.
Step 5: Confirm the REST API requires your key
Requests without a key, or with a wrong one, are refused; your key is accepted:
QDRANT_API_KEY=$(sudo sed -n 's/^QDRANT_API_KEY=//p' /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)"
echo "correct key -> HTTP $(curl -ks -o /dev/null -w '%{http_code}' -H "api-key: $QDRANT_API_KEY" https://127.0.0.1/collections)"
no key -> HTTP 401
wrong key -> HTTP 401
correct key -> HTTP 200
Step 6: Confirm the gRPC API requires your key
The gRPC API on port 6334 is TLS only and checks the same key, sent as api-key metadata. This lists collections over gRPC with curl and prints the gRPC status: 16 is UNAUTHENTICATED and 0 is OK.
QDRANT_API_KEY=$(sudo sed -n 's/^QDRANT_API_KEY=//p' /root/qdrant-info.txt)
grpc_list() { printf '\0\0\0\0\0' | curl -sk --http2 -o /dev/null -D - -H 'content-type: application/grpc' -H 'te: trailers' "$@" --data-binary @- https://127.0.0.1:6334/qdrant.Collections/List | tr -d '\r' | grep -i '^grpc-status'; }
echo "gRPC, no key: $(grpc_list)"
echo "gRPC, wrong key: $(grpc_list -H 'api-key: wrong')"
echo "gRPC, with key: $(grpc_list -H "api-key: $QDRANT_API_KEY")"
gRPC, no key: grpc-status: 16
gRPC, wrong key: grpc-status: 16
gRPC, with key: grpc-status: 0
From your own code, use any Qdrant client with TLS and the key. For example with the Python client (pip install qdrant-client), REST on 443 or gRPC on 6334:
from qdrant_client import QdrantClient
rest = QdrantClient(url="https://<public-ip>:443", api_key="<your-api-key>")
grpc = QdrantClient(host="<public-ip>", port=443, grpc_port=6334, https=True,
prefer_grpc=True, api_key="<your-api-key>")
print(rest.get_collections())
The certificate is self-signed, so either install a certificate your clients already trust (Step 12) or give your clients a copy of /etc/nginx/tls/qdrant.crt to trust (it covers the instance's IP addresses and DNS names).
Step 7: Create a collection and run your first vector search
Create a movies collection with 4 dimensional cosine vectors, add a few points with payloads (wait=true makes the write visible before you search), then ask for the two nearest neighbours of a query vector:
QDRANT_API_KEY=$(sudo sed -n 's/^QDRANT_API_KEY=//p' /root/qdrant-info.txt)
H="api-key: $QDRANT_API_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 -c '.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":"Notting Hill","genre":"romance"}},
{"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":"Paddington","genre":"family"}}
]}' | jq -c '.status'
curl -ks -X POST https://127.0.0.1/collections/movies/points/query -H "$H" -H 'Content-Type: application/json' \
--data '{"query":[0.85,0.15,0.1,0.1],"limit":2,"with_payload":true}' \
| jq -r '.result.points[] | "\(.payload.title) score=\(.score)"'
"ok"
"ok"
Interstellar score=0.99797505
Notting Hill score=0.2993925
Searches use Qdrant's Query API (POST /collections/<name>/points/query); the older points/search endpoint is deprecated.
Step 8: Use the read-only key
The read-only key can search but cannot write:
QDRANT_RO_KEY=$(sudo sed -n 's/^QDRANT_READ_ONLY_API_KEY=//p' /root/qdrant-info.txt)
R="api-key: $QDRANT_RO_KEY"
curl -ks -X POST https://127.0.0.1/collections/movies/points/query -H "$R" -H 'Content-Type: application/json' \
--data '{"query":[0.1,0.1,0.85,0.2],"limit":1,"with_payload":true}' | jq -r '.result.points[0].payload.title'
echo "read-only upsert -> HTTP $(curl -ks -o /dev/null -w '%{http_code}' -X PUT 'https://127.0.0.1/collections/movies/points?wait=true' \
-H "$R" -H 'Content-Type: application/json' --data '{"points":[{"id":9,"vector":[0.5,0.5,0.5,0.5]}]}')"
The Matrix
read-only upsert -> HTTP 403
Step 9: Filter by payload
Index the genre payload field, then combine vector similarity with a filter so only science fiction titles come back:
QDRANT_API_KEY=$(sudo sed -n 's/^QDRANT_API_KEY=//p' /root/qdrant-info.txt)
H="api-key: $QDRANT_API_KEY"
curl -ks -X PUT 'https://127.0.0.1/collections/movies/index?wait=true' -H "$H" -H 'Content-Type: application/json' \
--data '{"field_name":"genre","field_schema":"keyword"}' | jq -c '.status'
curl -ks -X POST https://127.0.0.1/collections/movies/points/query -H "$H" -H 'Content-Type: application/json' \
--data '{"query":[0.2,0.1,0.9,0.7],"filter":{"must":[{"key":"genre","match":{"value":"scifi"}}]},"limit":4,"with_payload":true}' \
| jq -r '.result.points[] | "\(.payload.title) score=\(.score)"'
"ok"
The Matrix score=0.8545449
Interstellar score=0.32867116
Step 10: Open the dashboard
Open the QDRANT_DASHBOARD_URL from the credentials file, for example https://<public-ip>/dashboard. The certificate is self-signed, so your browser shows a warning the first time; check the address is your instance's and continue. The dashboard asks for an API key: paste your QDRANT_API_KEY (or the read-only key to browse without being able to change anything) and choose Apply. The key is kept in your browser's local storage for this site.

Collections lists every collection with its status, point count, segments and vector configuration:

Open a collection to browse its points and payloads, find similar points or visualise the vectors:

The Info tab shows the collection's configuration and status:

The Console runs REST requests from the browser with your key and shows the response alongside:

The dashboard may show Update Available when a newer Qdrant release exists; see "Updates" below.
Step 11: Snapshots for backup and restore
A snapshot of a collection is written to /var/lib/qdrant/snapshots on the data volume, and can be downloaded with the API key. Creating, uploading and restoring snapshots needs the full API key; the read-only key is refused:
QDRANT_API_KEY=$(sudo sed -n 's/^QDRANT_API_KEY=//p' /root/qdrant-info.txt)
QDRANT_RO_KEY=$(sudo sed -n 's/^QDRANT_READ_ONLY_API_KEY=//p' /root/qdrant-info.txt)
curl -ks -X POST 'https://127.0.0.1/collections/movies/snapshots?wait=true' -H "api-key: $QDRANT_API_KEY" | jq -r '.result.name'
curl -ks https://127.0.0.1/collections/movies/snapshots -H "api-key: $QDRANT_API_KEY" | jq -r '.result[] | "\(.name) \(.size) bytes"'
echo "read-only snapshot -> HTTP $(curl -ks -o /dev/null -w '%{http_code}' -X POST https://127.0.0.1/collections/movies/snapshots -H "api-key: $QDRANT_RO_KEY")"
movies-4066997741393682-2026-10-20-01-10-05.snapshot
movies-4066997741393682-2026-10-20-01-10-05.snapshot 103936 bytes
read-only snapshot -> HTTP 403
To restore a collection from a snapshot hosted elsewhere, call PUT /collections/<name>/snapshots/recover with {"location": "https://..."} and the full API key. Qdrant downloads the file itself; it cannot reach the instance metadata service, so this cannot be used to read the instance's role credentials.
Step 12: Serve Qdrant under your own DNS name and certificate
Point a DNS record at the instance, then tell the instance its name. qdrant-set-host saves it to /etc/cloudimg/qdrant-hostname, makes a new self-signed certificate for it, updates nginx and the URLs in the credentials file:
sudo qdrant-set-host <your-domain>
To use a certificate from a public CA instead (for example from Let's Encrypt, with port 80 open to the internet while you request it), copy the certificate and key over the instance's own and reload nginx:
sudo apt-get install -y certbot
sudo systemctl stop nginx
sudo certbot certonly --standalone -d <your-domain>
sudo install -m 0644 /etc/letsencrypt/live/<your-domain>/fullchain.pem /etc/nginx/tls/qdrant.crt
sudo install -m 0600 /etc/letsencrypt/live/<your-domain>/privkey.pem /etc/nginx/tls/qdrant.key
sudo systemctl start nginx
qdrant-set-host replaces the certificate when it runs, so run it before you install your own certificate, and set the host name file first so the boot-time check keeps your name.
Step 13: Rotate the keys
To replace both keys (for example after a key was shared too widely), run:
sudo qdrant-set-keys
qdrant-set-keys: keys written to /root/qdrant-info.txt (root only)
New keys are written to /root/qdrant-info.txt and Qdrant is restarted with them; the old keys stop working at once. Update your applications and the dashboard with the new keys.
Configuration, first boot and updates
- Qdrant's settings live in
/etc/qdrant/config.base.yaml. After editing it, runsudo qdrant-set-keys --keepto render/etc/qdrant/config.yamlwith your current keys, thensudo systemctl restart qdrant. Keepservice:as a section in the file: the keys are added under it. - The first boot unit (
qdrant-firstboot) runs once.qdrant-set-hostruns at every boot and keeps the names and certificate in step with the instance's addresses (a stop and start can change the public IP) unless you set a fixed name. - Ubuntu security updates are applied automatically by unattended-upgrades. Qdrant itself is updated by replacing
/opt/qdrant/bin/qdrantwith a newer official release binary and restarting the service; take a snapshot first. Read the Qdrant release notes before upgrading across minor versions. - This image ships Qdrant 1.19.2, which includes the fix for GHSA-3gph-6c29-p29v (a read-only key or JWT accepted on the internal cluster API when
enforce_internal_authis enabled, affecting 1.18.0 to 1.19.1). The image runs a single node with clustering off. - Snapshots can also be stored in Amazon S3: in
config.base.yamlsetsnapshots_storage: s3and ans3_configsection (bucket, region,access_key,secret_key) understorage.snapshots_config. Give Qdrant an access key for that bucket: Qdrant cannot reach the instance metadata service, so it cannot use the instance's IAM role.
Support
cloudimg provides 24/7 technical support for this image by email and live chat: deployment, first login, HTTPS setup, upgrades, performance tuning and troubleshooting. Contact support@cloudimg.co.uk.
Qdrant is a trademark of its respective owner. cloudimg is not affiliated with or endorsed by the Qdrant project or its owner. All other product and company names are trademarks or registered trademarks of their respective holders.