Australian cloud for business software — built and run in Sydney since 2003

NetLeverage
NetLeverage

Backing up and restoring vector databases and model artefacts

Technical article
AI

About this article: AI-assisted writing, reviewed against the cited sources. This is source research, not a lab validation of the procedures. Check the stated versions and your own recovery requirements before making changes.

A vector backup that restores is three copies, not one. The database has to come back with its indexes in a state the server will accept. The embedding model, tokenizer and any adapter have to be the same bytes that produced those vectors. And the configuration that is not inside the data files — PostgreSQL's host-based authentication file, a Qdrant alias, the model's revision id — needs an explicit inventory: logical dumps, WAL archives and collection snapshots have different exclusions, whereas a physical base backup or full storage snapshot can include some of those files. No method should be assumed to contain every dependency.

An AI writing system produced this article from public sources. No backup, recovery or model load was run. This is a comparison of backup contents and recovery prerequisites, not an executable restore procedure. It does not authorise overwriting a database or changing a live cluster.

Why it matters

Vectors are coordinates from a particular model. Restore last January's table and this June's weights, and nearest-neighbour search still returns rows. They are neighbours in a space the new model does not use. The failure is silent: no checksum on the vectors compares them to the weight file.

The database half fails in a more ordinary way. A plain copy of a running PostgreSQL data directory without a consistent snapshot or backup protocol is not a reliable backup. A Qdrant snapshot from one node of a cluster is not a copy of the cluster. A logical dump of PostgreSQL rebuilds the HNSW index instead of restoring it, which is correct and slow.

How it works

Three PostgreSQL methods, and what they do to an index

PostgreSQL 18's backup chapter, current when opened on 10 November 2026, describes three methods. pgvector stores its values as column data and its HNSW or IVFFlat structures as indexes in that same cluster, and the project states that it uses the write-ahead log, so replication and point-in-time recovery apply to it. The mechanics below are PostgreSQL's.

SQL dump. pg_dump writes SQL, or a custom archive, that rebuilds one database as it was when the dump started. The dump is internally consistent. It does not block ordinary work. It does block operations that need an exclusive lock, and the chapter names most forms of ALTER TABLE. pg_dump is an ordinary client: it needs the right to read what you are dumping, which for a whole database almost always means a superuser. Its output can generally reload into a newer PostgreSQL, and it is the method the chapter says works across architectures, such as 32-bit to 64-bit.

A text dump and a custom archive use different restore clients, and neither is a complete recovery plan. PostgreSQL documents an empty database based on its pristine template, suitable roles and extensions, explicit error handling, and regenerated statistics as recovery prerequisites. Default client behaviour may continue after an error and leave a partial result; an all-or-nothing transaction can change that behaviour but also has compatibility and resource constraints. The reviewer of an actual recovery must choose those options for the exact dump, destination and extension versions. This article deliberately gives no creation or load command and no sequence to run against a database.

pg_dump does not write index contents. It writes the commands that recreate indexes. A file-system copy is larger for that reason, and the chapter says the file copy can be faster to take. On restore of a logical dump, every HNSW and IVFFlat index is built again. That build needs maintenance_work_mem and time, which is an operational fact of the method, not a property of the vectors. pg_dump -Fc is restored with pg_restore, not psql. Parallel dump (-j, directory format -F d) and parallel pg_restore -j exist for large databases. pg_dumpall adds roles and tablespaces, which pg_dump omits because they are cluster-wide. Each database inside a pg_dumpall run is consistent with itself. The databases are not one snapshot of one instant.

File copy. Copying the data directory with tar while the server is up does not produce a usable backup. The chapter gives two reasons: tar is not an atomic snapshot, and the server has its own buffers. An ordinary file-copy backup depends on a stopped server. Physical recovery has additional consistency and version requirements and can overwrite data; no replacement sequence is provided here. You also cannot lift one table's files out. Those files are unusable without pg_xact, the commit log, and restoring one table's commit log would make every other table wrong. A frozen snapshot of the volume can be taken while the server runs, if you trust the filesystem. A consistent frozen copy requires crash recovery and its associated WAL; checkpoint timing affects replay work. Those facts do not make an arbitrary live copy consistent. If the data files and the WAL, or the tablespaces, sit on different filesystems, the frozen snapshots have to be simultaneous or the technique does not hold. The chapter's alternatives are to shut down long enough to freeze every volume, or to use a base backup under continuous archiving.

Continuous archiving. WAL in pg_wal/ records every change so a crash can replay from the last checkpoint. Archive those segments and you can replay a base backup forward, stop at a chosen time, or feed the stream to a standby. pg_dump cannot be the base. It is logical, and WAL replay has nothing to apply it to. Recovery is of the whole cluster. You need an unbroken WAL sequence back to the start of the base backup, so a recoverability assessment must establish that archiving was working before the required base-backup start.

Segments are normally 16 MB, set at initdb. Without archiving they are recycled. Its prerequisites include an appropriate WAL level, enabled archiving and a correctly implemented archive command or library; configuration changes and testing are outside this comparison. The command runs as the PostgreSQL user. It must exit 0 only when the segment is safely stored. A 0 exit is permission to recycle the segment. A non-zero exit is a retry. If the archive fills up, pg_wal/ fills up, and a full filesystem causes a PANIC shutdown. Committed transactions are not lost. The server stays down until there is space. The archive holds the whole database; the chapter says to keep it unreadable by group and world.

GNU cp -i is a trap the chapter spells out: when the target exists, GNU cp -i still exits 0, so PostgreSQL will believe the segment was archived. The archival implementation must distinguish a safely stored identical segment from a conflicting destination; a filename check alone is not proof of durable storage. If a previous archive of the same name has different bytes, the command must fail. wal_level of minimal skips WAL for some bulk commands. Archiving or streaming during those commands does not have enough WAL to recover them. Crash recovery is unaffected. wal_level changes only at server start.

archive_command runs when a segment fills. A quiet server can leave a committed transaction unarchived for a long time. archive_timeout forces a switch. The switched file is still a full-sized segment, so a very short timeout bloats the archive. The chapter calls a minute or so usually reasonable. Archiving does not copy postgresql.conf, pg_hba.conf or pg_ident.conf. Those are edited outside SQL. The recovery inventory must identify their protected copies and the intended authentication rules.

What a Qdrant snapshot is, and what it leaves behind

Qdrant's snapshots page, opened the same day, defines a snapshot as a tar archive of one collection on one node at one time, available since v0.8.4. In a cluster you create one per node. A snapshot from node A does not contain node B's replica. A collection snapshot holds that collection's configuration, points and payloads. It does not hold aliases. The page says Cloud users may prefer Backups, described there as physical disk copies, for disaster recovery.

Restore is version-locked. A snapshot restores to the same minor version, or the next minor version. The page's example: a snapshot from v1.18.1 restores to v1.18.x only where the patch is 1 or higher, or to v1.19.x. It does not restore to an older patch of 1.18, and the example does not extend to 1.20.

Priority decides a conflict, and the default is the wrong one for a new collection. replica prefers data already on the node. snapshot prefers the archive. no_sync skips the extra synchronisation; the page says using it wrongly leaves the cluster broken. On a new, empty collection, replica keeps the empty collection, because the existing side was preferred and it had no points. A restore that "succeeds" and shows no vectors is that default. Choosing archive priority can replace existing data; deciding and validating that behaviour is outside this article. Recovering from a URL requires the node to fetch that URL. The page says Qdrant Cloud blocks outbound traffic, so URL restore is not available there. Upload-and-recover is the path they point at for a move between clusters.

Qdrant also documents startup recovery for single-node deployments. Its overwrite option can replace an existing collection. That path is not supported for a multi-node deployment or Cloud, and no startup or force-replacement command is supplied here.

A full storage snapshot includes aliases. It is only for a single node. Distributed mode is not supported, because the archive does not hold the files that mode needs. Cloud can create and download one, and cannot restore a cluster from it, because that restore uses the CLI. The page points Cloud recovery at Backups instead.

Deleting a collection snapshot is a separate call, available since v1.0.0. A snapshot left on the same disk as the data is not a backup of that disk.

The model file is a different object

Safetensors, in the project README on the main branch, is a tensor file that does not use pickle. The README's comparison marks pickle, the PyTorch default it is replacing, as unsafe because it runs arbitrary code. The file layout is an 8-byte little-endian length, then that many bytes of UTF-8 JSON, then a byte buffer. The header must start with {. Each tensor entry has a dtype, a shape and a pair of offsets into the buffer. Offsets are relative to the buffer, not to the start of the file. A __metadata__ key may hold only string-to-string pairs. Duplicate keys are disallowed. The buffer must be fully indexed, with no holes, which is how the format refuses a polyglot file. The header is capped at 100 MB. Tensor values are not checked: NaN and infinities are allowed. The README's own limit on "zero-copy" is that a GPU load still copies; the saving is not having to instantiate every tensor on CPU first. The format specification does not establish a current library release; the backup record must pin the actual library and artefact versions. Pin the file format and the library version in the backup record. They are not the same thing.

That file is weights. The format does not require a tokenizer, chat template or embedding implementation; string metadata can carry arbitrary descriptive text but is no substitute for a complete, versioned model bundle. A restore procedure that copies model.safetensors and not the tokenizer will load tensors and then fail, or worse, pair them with a tokenizer from another revision. Record the revision you embedded with — a commit id, a file hash — next to the database backup. The vector column cannot tell you later.

An adapter is another tensor file, meaningful only when added to the base weights it was trained on. Store it beside that base revision. A backup of the adapter alone is not a backup of the model.

What to choose and why

You need Use You still have to copy separately
A portable copy of one PostgreSQL database, including across a major version pg_dump of that database, plus pg_dumpall --globals-only if roles matter The extension installed on the destination, postgresql.conf and pg_hba.conf, and the model files. Budget an index rebuild
A point-in-time recovery objective Compatible base backup and an unbroken WAL chain back at least to its start Recovery target after backup completion, timeline and chain handling, and further procedures in the PostgreSQL chapter; no exact-time recovery is promised here
One Qdrant collection moved to a new cluster Per-node snapshots and a separately validated recovery plan for the supported version window Aliases. On Cloud, do not plan on a URL fetch
The whole single-node Qdrant store, aliases included A full storage snapshot, restored only onto a single node A distributed cluster. The page says that mode is unsupported for this archive
Reduced executable deserialisation surface A data-only tensor format such as safetensors plus authenticated, version-matched tokenizer and configuration files The vector table. The weight file does not contain it

Take the database backup and the model bundle in the same change window, and write both names on the restore sheet. Restoring one without the other is a successful job that searches the wrong space.

A separate recovery-validation plan should use an isolated destination and explicitly protect the surviving data. Acceptance evidence includes the recovered schema and extension versions, roles and access policy, row or point counts, representative payloads, expected exact-search results, model-bundle hashes and measured elapsed time. A successful command or HTTP response alone proves none of those. The person approving the plan must decide how to handle partial failure, version incompatibility and a failed acceptance check before any restore is attempted. No live restore sequence is included in this article.

Risks and limits

  • A live tar of the PostgreSQL data directory is not a backup. The server has to be shut down, or you need a frozen snapshot that includes the WAL, on every filesystem the cluster uses, taken together.
  • archive_command exiting 0 tells PostgreSQL it may recycle that WAL segment. GNU cp -i exiting 0 when the file exists is the chapter's example of a false success.
  • Logical restore rebuilds indexes. HNSW build time and memory are part of the recovery time. A file-level or PITR restore keeps the index files and is tied to the PostgreSQL version.
  • Qdrant priority defaults to replica. On a new collection that keeps the empty side. Skipping synchronisation or forcing replacement can damage consistency or overwrite data; neither is prescribed here.
  • A collection snapshot has no aliases, and one node's snapshot is not the cluster. Full storage snapshots do not cover distributed mode.
  • Safetensors permits NaN and does not require a tokenizer bundle. A header under 100 MB and a hole-free buffer are integrity rules for the tensor file, not a check that you have the right model for the vectors.
  • No backup in this article was executed. PostgreSQL 18 and the Qdrant page were the texts opened on 10 November 2026. A later minor version can narrow the snapshot restore window; the page's rule is same minor, or the next, and the v1.18.1 example does not jump two minors.
  • No NetLeverage product behaviour is claimed.

Sources

Claim-level notes are in the evidence file. Opened 10 November 2026.

  1. PostgreSQL 18, Chapter 25 — https://www.postgresql.org/docs/current/backup.html
  2. PostgreSQL 18, SQL dump — https://www.postgresql.org/docs/current/backup-dump.html
  3. PostgreSQL 18, file-system backup — https://www.postgresql.org/docs/current/backup-file.html
  4. PostgreSQL 18, continuous archiving — https://www.postgresql.org/docs/current/continuous-archiving.html
  5. Qdrant snapshots — https://qdrant.tech/documentation/snapshots/
  6. safetensors README — https://github.com/huggingface/safetensors/blob/main/README.md
  7. pgvector README, on WAL — https://github.com/pgvector/pgvector/blob/master/README.md