Is your own server the right home for Qdrant?
Yes, if you want your embeddings on hardware you control and can live with a short pause during upgrades. Qdrant is the part of a retrieval app that stores vectors and finds the nearest ones fast. It runs best when its search structures sit in memory and everything else comes off a quick local disk. A whole machine gives it exactly that: no other customer's programs eat into the memory, and the disks are inside the box rather than across a network.
The disk part matters more than it sounds. Qdrant's installation guide asks for storage the operating system sees as a block device, formatted with a POSIX file system, and rules out NFS shares and S3-style object stores. A dedicated server is a single physical computer that runs only your work, with root access, its own disks and an unmetered port, so copying backups off it every night never turns into a bandwidth bill.
What you give up is high availability. For that, Qdrant suggests at least three nodes with every collection stored twice: then a rolling restart upgrades it without downtime. One server means one copy of your data, protected by backups, and a short pause at every upgrade.
How much memory will your vectors need?
Count your vectors and their length first, because that decides almost everything. Qdrant's capacity-planning guide multiplies points by dimensions by bytes per number (4 for ordinary float32), then adds the search graph, a per-point tracker of 52 bytes, any indexes on fields you filter by, and about 20% spare room for the operating system and background work. The JSON you attach to each point stays on disk by default.
| Your situation | Rough memory | Where the figure comes from |
|---|---|---|
| 1 million vectors of 768 numbers, stored twice across a cluster | about 7.3 GB | The guide's own worked example, spare room included |
| The same vectors on one server, stored once | about 3.7 GB | Our arithmetic with the guide's formula |
| 4-bit TurboQuant copy kept pinned in memory, originals moved to the cold tier on disk | the 5.72 GB of raw vectors in that example drops to about 0.72 GB | The guide's quantization example |
Compression (quantization) trades a little accuracy for a lot of memory, so compare search results before and after; the docs warn that some embedding models compress badly. You can also leave vectors in the cold memory tier on disk. The guide's rule of thumb: cut the vectors held in memory by half and searches take about twice as long. Treat roughly 100 million vectors as a loose ceiling for one machine, and keep disk in reserve: the docs say a snapshot restore wants double the space the collection occupies, and during the restore below your old data folder and the snapshot file stay on disk too.
The server that fits
Choose the memory first, then insist on solid-state disks; Qdrant's production checklist warns that hard drives slow searches badly.
- Getting started, or a knowledge base of a few million text passages: the Xeon E3-1585L v5 server with 16 to 64 GB and an NVMe drive, or the Xeon D-1587, which pairs 16 cores with 16 to 128 GB of memory and a 1.92 TB NVMe drive. The NVMe dedicated server page explains the drive choices.
- Bigger collections: the two-processor Xeon E5-2630L v3 and Xeon Gold 6130 servers, up to 256 GB on their cards, with SSDs picked on the order page. The AMD EPYC servers, made to order, reach 1 TB.
- Running on ARM: pick the Ampere Altra Q64-22, with 64 cores and anywhere from 32 GB up to 1 TB, made to order. Qdrant supports arm64. The small X-Gene cartridge is not a match: it only runs Ubuntu 16.04 or 18.04, older than the Docker release this guide needs.
A ready server is handed over within 30 minutes of approval; a server built to the spec you choose takes 4 to 24 hours when we hold the parts and longer when they must be bought in (the Ampere Altra takes one to two weeks). If you want a console in case a firewall rule ever shuts SSH out, go for a rack server or a blade, or the m510 or m710x cartridge: those include out-of-band console access, and every other cartridge can only be switched on and off remotely.
Up and running in an evening
Qdrant's security guide is blunt: as installed, it answers on every network address and checks no credentials at all. So here the keys exist before the database first starts, and Qdrant answers only on the server itself. First install Docker Engine (release 28 or newer) plus its Compose plugin, following Docker's guide for Ubuntu. The block below then does four things:
- Switches on the firewall, keeping SSH open, for IPv4 and IPv6 alike.
- Creates a private folder,
/opt/qdrant, with room for data and snapshots. - Generates two random keys, a full-access one and a read-only one, into a file only root can read. If that file already exists it is left alone, so a second run never swaps out keys your apps already use.
- Writes the Compose file and starts Qdrant, but only on Docker 28 or later, where a port tied to 127.0.0.1 really stays local.
sudo sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
sudo ufw allow OpenSSH && sudo ufw --force enable
sudo install -d -m 700 /opt/qdrant /opt/qdrant/data /opt/qdrant/snapshots
if sudo test -e /opt/qdrant/keys.env; then echo "keys already there, leaving them"
else k1=$(openssl rand -hex 32) && k2=$(openssl rand -hex 32) && [ -n "$k1" ] && [ -n "$k2" ] \
&& sudo install -m 600 /dev/null /opt/qdrant/keys.env \
&& printf 'QDRANT__SERVICE__API_KEY=%s\nQDRANT__SERVICE__READ_ONLY_API_KEY=%s\nQDRANT__TELEMETRY_DISABLED=true\n' "$k1" "$k2" \
| sudo tee /opt/qdrant/keys.env >/dev/null; fi
sudo tee /opt/qdrant/compose.yaml >/dev/null <<'EOF'
services:
qdrant:
image: qdrant/qdrant:v1.20.0
restart: unless-stopped
env_file: /opt/qdrant/keys.env
ports:
- "127.0.0.1:6333:6333"
- "127.0.0.1:6334:6334"
volumes:
- /opt/qdrant/data:/qdrant/storage
- /opt/qdrant/snapshots:/qdrant/snapshots
ulimits:
nofile: {soft: 10000, hard: 10000}
EOF
ver=$(sudo docker version --format '{{.Server.Version}}') && [ "${ver%%.*}" -ge 28 ] \
&& sudo docker compose -f /opt/qdrant/compose.yaml up -d \
|| echo "NOT STARTED: needs Docker Engine 28+, or the start failed"
This is our version of the Compose example in Qdrant's installation guide. What we changed: a fixed version instead of latest, both ports tied to 127.0.0.1 instead of every address, no expose list (port 6335 only matters for clusters), environment variables instead of the inline configs block, no container_name line, full folder paths under /opt/qdrant for the data plus a second one for snapshots, an open-files limit of 10000 (the troubleshooting page's fix for the 'too many files open' error), and unless-stopped in place of always. The last variable switches off the anonymized usage statistics the image sends by default.
To check it worked, curl -s http://localhost:6333/readyz should answer straight away, since health checks never need a key, while a request for /collections without a key should be refused. An app on the same server talks to http://127.0.0.1:6333 and sends its key in an api-key header.
Keeping keys and data private
Give each part of your app only the key it needs. The read-only key can search and read but cannot add, change or delete anything, which suits the code that answers user questions. The full key belongs to whatever loads your documents. It can also make Qdrant download a snapshot from any web address you give it, so it should never sit in a browser or a public repository. If several teams share one server, add QDRANT__SERVICE__JWT_RBAC=true to keys.env then rebuild the running container so it reads the change: docker compose -f /opt/qdrant/compose.yaml up --force-recreate -d (a simple restart would keep the old settings); you can then sign tokens limited to single collections with the full key, using any JWT library or the Access Tokens screen in the dashboard.
Keys must not travel over plain HTTP, the docs warn, or anyone along the way can read them. If your app lives on another machine, put a web server with a certificate in front. One way, on Ubuntu 24.04 or later, is Caddy, which fetches the certificate by itself once your hostname points at the server. This configuration is ours, and it also keeps the built-in dashboard off the internet:
sudo apt install -y caddy
sudo ufw allow 80,443/tcp
sudo tee /etc/caddy/Caddyfile >/dev/null <<'EOF'
vectors.example.com {
@dash path /dashboard /dashboard/*
respond @dash 404
reverse_proxy 127.0.0.1:6333
}
EOF
sudo systemctl reload caddy
For the dashboard itself, run ssh -L 16333:localhost:6333 you@your-server on your own computer and visit http://localhost:16333/dashboard while the tunnel is open. Any account allowed to use docker inspect on the server can read the keys, so keep that access to yourself.
Backups you can restore
With a single server, a full storage snapshot captures every collection and alias at once. POST /snapshots with the full key creates one in /opt/qdrant/snapshots. This nightly root job does that, passing the key through standard input, which keeps it off any command line, and archives compose.yaml and keys.env into a folder only root can read:
sudo install -d -m 700 /var/backups/qdrant
sudo tee /usr/local/sbin/qdrant-backup.sh >/dev/null <<'EOF'
set -euo pipefail
umask 077
day=$(date +%F)
sed -n 's/^QDRANT__SERVICE__API_KEY=/api-key: /p' /opt/qdrant/keys.env \
| curl -fsS -X POST -H @- http://127.0.0.1:6333/snapshots > /var/backups/qdrant/snapshot-$day.part
mv /var/backups/qdrant/snapshot-$day.part /var/backups/qdrant/snapshot-$day.json
tar -czf /var/backups/qdrant/config-$day.tar.gz -C /opt/qdrant compose.yaml keys.env
EOF
printf '15 4 * * * root bash /usr/local/sbin/qdrant-backup.sh >> /var/backups/qdrant/backup.log 2>&1\n' \
| sudo tee /etc/cron.d/qdrant-backup >/dev/null
Each day's JSON file names that night's snapshot. Copy /opt/qdrant/snapshots and the backup folder to a different machine with rsync -a, never with --delete, and once you have checked that copy, clear out old snapshots by sending DELETE /snapshots/NAME.
A full snapshot can only be restored when Qdrant starts, through its --storage-snapshot option. Working as root, shut it down with docker compose -f /opt/qdrant/compose.yaml down. Move the current data aside with test ! -e /opt/qdrant/data.old && mv /opt/qdrant/data /opt/qdrant/data.old && install -d -m 700 /opt/qdrant/data && echo moved, and stop there if it does not say moved. Next, launch a one-off container with the restore flag: docker compose -f /opt/qdrant/compose.yaml run --service-ports --rm qdrant, adding ./qdrant --storage-snapshot /qdrant/snapshots/NAME.snapshot to the end of that line. Whatever you find, press Ctrl+C in that window and wait for the one-off container to exit first. If your collections looked right, bring Qdrant back normally with up -d. If not, roll back: docker compose -f /opt/qdrant/compose.yaml down, then rm -rf /opt/qdrant/data && mv /opt/qdrant/data.old /opt/qdrant/data, then up -d. Delete data.old only once you are happy.
One catch: snapshots can be restored only into the Qdrant minor version they came from or the one after it, so restore with the version written in your archived Compose file.
Upgrading without surprises
Step through the minor versions in order, landing on the newest patch of each. Qdrant's upgrade guide says a single node cannot skip versions, or some data migrations may not run. Take a snapshot, change the image version in compose.yaml, pull the new image with docker compose -f /opt/qdrant/compose.yaml pull and restart with up -d. Expect a short pause while it restarts. Upgrade the server before your client libraries, and keep those within one minor version of the server.
Free to use, and when to look elsewhere
The Apache 2.0 licence comes from the LICENSE file in Qdrant's repository. Read this as our own interpretation rather than legal advice: you may use and change the software freely; if you hand copies to others, include the licence and copyright notices; and nothing obliges you to release your own code.
If a restart window is unacceptable, or your data outgrows one machine, you need a cluster, and Qdrant points cluster users to Kubernetes or its own cloud. If you are still choosing the rest of your stack, our pages on hosting the language model and on running Dify yourself cover the other pieces of a retrieval app.
Questions people ask
- Does self-hosting Qdrant cost anything in licences?
- No. The engine is open source under Apache 2.0, so you pay only for the server it runs on. Qdrant's managed cloud and its enterprise operator for private clouds are the vendor's commercial offerings, and you do not need either to run it yourself.
- What are the minimum server requirements for Qdrant?
- Qdrant does not publish a fixed minimum. It needs a 64-bit x86 or ARM processor, block-level storage (a local disk, or network block storage such as iSCSI), formatted with a POSIX file system, and SSD or NVMe disks if vectors are kept on disk. The memory depends on how many vectors you store and how long they are, which the capacity formula works out.
- Where do I put the API key in a Docker setup?
- Pass it as the
QDRANT__SERVICE__API_KEYenvironment variable, ideally from a file only root can read, as in the setup above;QDRANT__SERVICE__READ_ONLY_API_KEYadds a search-only key. Clients then send it in theapi-keyheader. Use HTTPS whenever the key crosses a network. - Can I open the Qdrant dashboard on my self-hosted server?
- Yes. Qdrant serves its Web UI at
/dashboardon port 6333. In the setup on this page that port is local only, so reach it through an SSH tunnel from your own computer rather than exposing it to the internet. - Will a network drive or S3 bucket work for Qdrant's data?
- Not for the database itself. Qdrant's guide rules out NFS mounts and S3-style object storage for its data; network storage that offers block-level access, such as iSCSI, is accepted, but the simplest choice is the server's own disks. Snapshot files alone can be sent to S3-compatible storage if you configure that.