Databases AWS

Qdrant on AWS User Guide

| Product: Qdrant on AWS

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, with the REST API 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.

Qdrant's vector storage, collection segments and snapshots live on a separate, independently resizable EBS volume mounted at /var/lib/qdrant, so the database tier is kept off the operating system disk and can be grown without disturbing the rest of the instance.

On the first boot of every deployed instance, a one shot service generates a fresh Qdrant API key, writes it into /etc/qdrant/qdrant.env (consumed by qdrant.service via an EnvironmentFile drop-in), 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.

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

Step 1: 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 cloudimg listing and choose Select, then Continue on the subscription summary.

Pick an instance type of m5.large or larger as a balanced default; 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, and either 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.

Step 2: Launch the Instance from the AWS CLI

The following block launches an instance from the cloudimg 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, <subnet-id> with your subnet ID, and <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 on the CLI.

Step 3: 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:

Operating system variant SSH login user
Ubuntu 24.04 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>

Step 4: Retrieve the Per Instance API Key

On the very first boot, qdrant-firstboot.service generates an API key unique to this instance, writes it into /etc/qdrant/qdrant.env, 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.txt

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

If you ever need to rotate the API key, edit /etc/qdrant/qdrant.env and replace the QDRANT__SERVICE__API_KEY value, then sudo systemctl restart qdrant.service.

Step 5: Confirm the Service and Probe Health

The Qdrant daemon and the nginx reverse proxy run as systemd services and start automatically at boot. Confirm both are active:

systemctl is-active qdrant.service nginx.service

The 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/healthz

The expected output is healthz check passed.

Confirm 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/collections

The api-key header guard runs at the nginx edge, so it works whether your client connects to the REST surface on port 80 or on port 6333.

Step 6: Create a Collection and Upsert Vectors

The recipes below read the api-key out of /root/qdrant-credentials.txt at the top of each block, so they work in a fresh shell session and in any automation that runs each block independently. Each block first sets the local KEY variable, 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}.

Step 7: 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/collections

The expected response is {"result":{"collections":[{"name":"my-vectors"}]},"status":"ok","time":4.1e-6}.

Step 8: 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 it 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. The official Qdrant Python client picks this up automatically, for example:

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 exposing gRPC to the open internet.

Step 9: 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/qdrant

The expected output reports the volume mounted as ext4:

TARGET          SOURCE       FSTYPE OPTIONS
/var/lib/qdrant /dev/nvme1n1 ext4   rw,relatime
df -h /var/lib/qdrant

Because 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, then extend the filesystem on the instance with sudo resize2fs <device>.

Step 10: 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 cloudimg 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 editing it:

sudo systemctl restart qdrant.service

To 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/snapshots

Snapshots 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 are done 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-vectors

Screenshots

Listing collections over the REST API

Querying the Qdrant REST API with the api-key header to list collections on a freshly launched instance.

Inserting a vector with payload

Upserting a vector point with its payload into a Qdrant collection through the REST API.

Nearest neighbour vector search

Running a nearest neighbour search query against a Qdrant collection and inspecting the results.


Support

This Amazon Machine Image is provided by cloudimg with 24/7 technical support by email and chat. Contact cloudimg 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.