VPS Snaps

How to back up a Qdrant vector database

Qdrant backs up through its snapshot API. POST /collections/<name>/snapshots writes a .snapshot archive of one collection while Qdrant keeps serving, and POST /snapshots captures every collection plus aliases on a single node; download each file and keep it off the server. Restore by uploading it to /collections/<name>/snapshots/upload?priority=snapshot, or at startup with --snapshot, into the same minor version or the next one.

8 min readUpdated Checked against official documentation

What to back up

Qdrant is open source (Apache 2.0); 1.19.1, from September 2026, is current. It has no snapshot schedule or retention of its own; a script does both. The choices:

MethodContainsUse it when
Collection snapshotOne collection's config, points, payloads and indexes on this node; no aliasesAlmost always; works on clusters too
Full storage snapshotEvery collection plus aliasesA single node you would rebuild whole; restores only at startup
Re-embed from sourceNothing stored: you rebuild vectors from the original dataThe model changed, or the snapshot can't reach your version

Snapshots never include the server config, which holds API keys and TLS settings (config.yaml, or QDRANT__ variables in your compose file). Back it up encrypted.

Files land in snapshots_path: /qdrant/snapshots in the Docker image, /var/lib/qdrant/snapshots with the .deb package (/etc/qdrant/config.yaml). Docker examples mount only /qdrant/storage, so add -v /srv/qdrant/snapshots:/qdrant/snapshots. A snapshot is an uncompressed tar, roughly the collection's size on disk, written in storage/snapshots_temp (temp_path changes that) and then moved: a 12 GB collection needs about 12 GB free on each disk.

Keys and permissions

Self-hosted Qdrant accepts anyone who can reach port 6333 until you set service.api_key (or QDRANT__SERVICE__API_KEY), sent in an api-key header; keep the port private or use TLS. Who can do what:

ActionAdmin keyRead-only keyJWT with read-write on the collection
Create or delete a collection snapshotYesNoYes
List or download a collection snapshotYesYesYes
Upload or recover a snapshotYesNoNo
Create a full storage snapshotYesNoNo

The commands below read the URL and admin key from a root-only file (chmod 600); load it with . /etc/qdrant-backup.env:

/etc/qdrant-backup.env
QDRANT_URL=http://127.0.0.1:6333
QDRANT_API_KEY=your-admin-api-key

Take a collection snapshot

Terminal
curl -fsS -X POST -H "api-key: $QDRANT_API_KEY" "$QDRANT_URL/collections/products/snapshots?wait=true"

The reply's result holds the file name (<collection>-<peer id>-<date-time>.snapshot), its size and a SHA-256 checksum, also written beside it as <name>.checksum. On a large collection, wait=false returns at once and the file appears in the list when done. List, download and delete:

Terminal
curl -fsS -H "api-key: $QDRANT_API_KEY" "$QDRANT_URL/collections/products/snapshots"
Terminal
curl -fsS -H "api-key: $QDRANT_API_KEY" -o products.snapshot "$QDRANT_URL/collections/products/snapshots/products-5627091348216794-2026-10-04-02-15-00.snapshot"
Terminal
curl -fsS -X DELETE -H "api-key: $QDRANT_API_KEY" "$QDRANT_URL/collections/products/snapshots/products-5627091348216794-2026-10-04-02-15-00.snapshot"

Downloading works only over the REST API, not gRPC. A snapshot is a tar archive: tar -tf products.snapshot | head lists it, and comparing sha256sum products.snapshot with the checksum proves the download is complete.

Full storage snapshots and S3

POST /snapshots snapshots every collection and saves the aliases, as full-snapshot-<date-time>.snapshot in snapshots_path. GET /snapshots, GET /snapshots/<name> and DELETE /snapshots/<name> list, download and delete. It is for single nodes only: distributed mode isn't supported, and a full snapshot restores only at startup.

Since 1.10, Qdrant can write snapshots to S3-compatible storage instead of the local disk. Listing, download and recovery work as before:

config/production.yaml (Docker) or /etc/qdrant/config.yaml
storage:
  snapshots_config:
    snapshots_storage: s3
    s3_config:
      bucket: acme-qdrant-snapshots
      region: eu-central-1
      access_key: ACCESS_KEY_ID
      secret_key: SECRET_ACCESS_KEY

For S3-compatible storage outside AWS, add endpoint_url with the provider's endpoint. The keys can come from QDRANT__STORAGE__SNAPSHOTS_CONFIG__S3_CONFIG__ACCESS_KEY and ..._SECRET_KEY instead. A bucket at another provider gives you the off-site copy in one step (bucket setup); old snapshots still pile up until you delete them.

Run it every night

This script snapshots every collection, downloads and checksums each file, deletes the server's copy, saves the aliases (collection snapshots leave them out) and keeps two days. It needs jq.

/usr/local/bin/qdrant-backup.sh
#!/bin/bash
set -euo pipefail
. /etc/qdrant-backup.env
DEST=/var/backups/qdrant
H="api-key: $QDRANT_API_KEY"

collections=$(curl -fsS -H "$H" "$QDRANT_URL/collections" | jq -r '.result.collections[].name')

for c in $collections; do
  resp=$(curl -fsS -X POST -H "$H" "$QDRANT_URL/collections/$c/snapshots?wait=true")
  name=$(echo "$resp" | jq -r '.result.name')
  sum=$(echo "$resp" | jq -r '.result.checksum')
  mkdir -p "$DEST/$c"
  curl -fsS -H "$H" -o "$DEST/$c/$name" "$QDRANT_URL/collections/$c/snapshots/$name"
  echo "$sum  $DEST/$c/$name" | sha256sum -c --quiet -
  curl -fsS -X DELETE -H "$H" "$QDRANT_URL/collections/$c/snapshots/$name" > /dev/null
  echo "$(date -Is) $c: $name"
done

curl -fsS -H "$H" "$QDRANT_URL/aliases" > "$DEST/aliases-$(date +%F).json"
find "$DEST" -type f -mtime +1 -delete
Terminal
sudo chmod 700 /usr/local/bin/qdrant-backup.sh
/etc/cron.d/qdrant-backup
15 2 * * * root /usr/local/bin/qdrant-backup.sh >> /var/log/qdrant-backup.log 2>&1

Copy /var/backups/qdrant to another provider (3-2-1 rule), see cron schedules for failure alerts, and restore a snapshot on a spare server monthly to time it (testing restores).

Restore a collection

Upload from any machine that reaches the API. The name in the URL is the target, created if missing; restore beside the live collection first:

Terminal
curl -fsS -X POST -H "api-key: $QDRANT_API_KEY" -F "snapshot=@/var/backups/qdrant/products/products-5627091348216794-2026-10-04-02-15-00.snapshot" "$QDRANT_URL/collections/products_restored/snapshots/upload?priority=snapshot"

A file already on the node restores with PUT /collections/<name>/snapshots/recover and a body such as {"location": "file:///qdrant/snapshots/products/products-...snapshot", "priority": "snapshot"}; since 1.17.1 it must sit inside snapshots_path. The location can also be an http(s) URL the node can reach, unless service.enable_snapshot_url_recovery is false. Both endpoints accept a SHA-256 checksum to verify first.

  • priority matters when other replicas of the shard exist: snapshot makes the file the source of truth, replica keeps the existing data and resyncs from it, no_sync skips synchronisation. The snapshots page lists replica as the default, but the code has defaulted to snapshot since 1.9.3. Pass it explicitly.
  • Re-create aliases from your saved JSON with POST /collections/aliases and {"actions": [{"create_alias": {"collection_name": "products", "alias_name": "products-live"}}]}.
  • Check the result: POST /collections/products_restored/points/count with {"exact": true} returns the point count to compare with the original.

On a single node you can also restore at startup. With Docker, run once without publishing the port; when it has started, stop it with Ctrl+C and start your normal container, which finds the collection in the storage volume:

Terminal
docker run --rm -v /srv/qdrant/storage:/qdrant/storage -v /srv/qdrant/snapshots:/qdrant/snapshots qdrant/qdrant:v1.19.1 ./qdrant --snapshot /qdrant/snapshots/products/products-5627091348216794-2026-10-04-02-15-00.snapshot:products

--snapshot <file>:<collection> can repeat; the collection must not exist unless you add --force-snapshot, which replaces it. Never leave either flag in a compose file: with --snapshot alone the next restart stops because the collection exists, and with --force-snapshot every restart overwrites it again. A full storage snapshot restores the same way with --storage-snapshot /qdrant/snapshots/full-snapshot-2026-10-04-02-15-00.snapshot.

Versions and upgrades

A snapshot restores into the same minor version or the next: one from 1.18.1 goes into 1.18.1 or later 1.18 releases, or 1.19.x, not 1.20. Upgrades can't skip minor versions either (1.17 to 1.19 goes through the latest 1.18 patch). Pin the image tag (qdrant/qdrant:v1.19.1, not latest) and snapshot before every upgrade.

To bring an older snapshot forward, restore it into its own version on a spare server and upgrade one minor version at a time. Or copy points between two running instances with Qdrant's migration tool (its qdrant command, with --source.url and --target.url), which reads and writes through the gRPC API on port 6334 rather than copying storage files.

Distributed clusters

  • Each node's snapshot holds only that node's shards: create one on every node and keep the files apart by node.
  • Restore by uploading each node's file to the same node, with priority=snapshot. Full storage snapshots and --snapshot are for single nodes only.
  • /collections/<name>/shards/<shard_id>/snapshots snapshots and restores one shard.
  • The internal port, 6335, is never protected by the API key; keep it reachable only by other nodes.

When re-embedding is the backup

A snapshot saves vectors you paid to compute. Take 8 million chunks of about 400 tokens: rebuilding means embedding 3.2 billion tokens again, billed per token by an API or bound by your GPU. If your account allows 1 million tokens a minute, that is 3,200 minutes, about 53 hours. The snapshot holds 8,000,000 × 1,536 dimensions × 4 bytes = 49.2 GB of raw float32 vectors, plus payloads and indexes, and restores at disk and network speed.

Re-embedding is the better route when the collection is small (50,000 chunks is 20 million tokens), when the source data is already backed up, as with a PostgreSQL dump, or when the snapshot can't reach your Qdrant version. It is the only route when the embedding model changes or is retired: queries must be embedded by the same model as the stored vectors. Keep the model name and version, vector size, distance and chunking settings in your repository with the code.

Common errors

ErrorFix
Must provide an API key or an Authorization bearer tokenAdd the api-key header. Invalid API key or JWT means the key is wrong.
Forbidden: Global manage access is requiredA read-only key can list and download, not create or restore. Use the admin key.
Collection products already exists. Use --force-snapshot to overwrite it.Startup restore into an existing collection. Restore under another name, or add --force-snapshot once.
Wrong input: Snapshot is not compatible with existing collection: Collection vectors: ...The target exists with other vector settings or shard count. Restore under a new name.
Wrong input: Snapshot checksum mismatch: expected ..., got ...The file is damaged or incomplete. Download it again.
Forbidden: Snapshot file path must be inside the snapshots directory ...Move the file under snapshots_path, or upload it instead.
Forbidden: Snapshot recovery from remote URLs is disabled in the configurationenable_snapshot_url_recovery is false. Upload the file.

Frequently asked questions

Can I back up Qdrant by copying its storage directory?
Qdrant documents snapshots as the way to back up and move collections, not copies of the storage directory. Use collection snapshots, or a full storage snapshot on a single node.
Does a Qdrant snapshot include aliases and API keys?
A collection snapshot includes neither. A full storage snapshot includes aliases. API keys live in the server config, so back that up separately.
Can I restore a Qdrant snapshot into a newer version?
Into the same minor version or the next one only, such as 1.18 into 1.19. For bigger jumps, restore into the old version and upgrade one minor version at a time.
How do I back up a Qdrant cluster?
Create a collection snapshot on every node, since each holds only its own shards, and restore each file to the same node with priority=snapshot.

How this was checked

Commands, limits and prices were checked against these official pages, on October 4, 2026: