Home / AI & GenAI / Vector Databases

AI & GenAIRunbookIntermediate

Pinecone serverless backup and restore runbook: on-demand and scheduled backups, restore jobs and the switch-over

Someone ran delete_all on the production namespace, or a bad re-embed overwrote good vectors. Take backups before risky changes, restore a backup into a new index, verify it and point the application at it - with the commands and checks for each step.

Deepak Bhardwaj4 min read

Applies toPinecone serverless indexes, Python SDK with backup support (pc.create_backup / create_index_from_backup); scheduled backups need a recent API version - check your SDK

When you need this

  • Accidental delete: delete_all=True on the wrong namespace, or a delete-by-filter that matched too much.
  • Bad data: a pipeline bug overwrote metadata or vectors with wrong values.
  • Before risky operations: re-embedding, metadata migrations, bulk deletes.

A restore creates a new index from the backup; it doesn't roll back the existing one in place. Plan for a host switch in the application.

Step 1: take backups before you need them

On-demand backup before a risky change:

from pinecone import Pinecone

pc = Pinecone(api_key=API_KEY)

backup = pc.create_backup(
    index_name="kb-prod",
    backup_name="kb-prod-before-reembed-2026-10-28",
    description="Before re-embedding with new chunking",
)
print(backup.backup_id, backup.status)

Wait until it's usable:

import time
while pc.describe_backup(backup_id=backup.backup_id).status != "Ready":
    time.sleep(10)

List backups:

for b in pc.list_backups(index_name="kb-prod"):
    print(b.backup_id, b.name, b.status, b.created_at, b.record_count)

Scheduled backups are available in newer API versions, with one active schedule per index and a required retention period. Set one up for production indexes, and keep on-demand backups for change windows.

Note

Backups cost storage. Delete on-demand backups you no longer need (pc.delete_backup(backup_id=...)), but only after the change they protected has been verified.

Step 2: also keep the source of truth

A vector backup restores what was in the index. It doesn't help if the bad data was there for a month. Keep these too:

  • Source documents (object storage, CMS, database) with versioning.
  • Chunk text and metadata in your own store, keyed by record ID.
  • Embedding model name and version per record.

With these you can always rebuild the index from scratch, which is the last-resort recovery path.

Step 3: during an incident, stop the damage

  1. Stop the job or deployment that caused it (pause ingestion, roll back the deploy).
  2. Note the time the problem started; you need a backup from before it.
  3. Don't take a new backup and then restore from it; it contains the damage.

Step 4: restore into a new index

pc.create_index_from_backup(
    backup_id="<backup_id from before the incident>",
    name="kb-prod-restored-20261028",
    deletion_protection="enabled",
)

for job in pc.list_restore_jobs():
    print(job.restore_job_id, job.status, job.percent_complete)

Poll the job:

job = pc.describe_restore_job(job_id="<restore_job_id>")
print(job.status, job.percent_complete)

The restored index is created in the same cloud and region as the backup by default. Cross-region restore has extra conditions (same cloud provider, matching dimension and metric, transfer charges); check the current documentation before relying on it for DR.

Step 5: verify before switching

restored = pc.Index(host=pc.describe_index("kb-prod-restored-20261028").host)
stats = restored.describe_index_stats()
print(stats.total_vector_count, {k: v.vector_count for k, v in stats.namespaces.items()})
Check Pass condition
Record count per namespace Matches the expected count from before the incident
Spot fetch Known IDs return with correct metadata
Retrieval quality Your evaluation question set returns the expected chunks
Filters A few filtered queries return results (metadata intact)

Step 6: switch the application

Keep the index host in configuration, never hard-coded:

PINECONE_INDEX_HOST=kb-prod-restored-20261028-xxxx.svc.<region>.pinecone.io

Roll the change, watch error rate and retrieval metrics, then:

  • Replay writes that happened after the backup was taken (documents changed since then) from your ingestion log or source system's change feed.
  • Keep the damaged index for a few days for investigation, with deletion protection on, then delete it.

Step 7: replay the gap

Records written between the backup time and the restore are missing from the restored index. Use your ingestion system:

changed = source.documents_changed_since(backup_created_at)
for doc in changed:
    reingest(doc, index=restored)

This is why idempotent ingestion with deterministic IDs matters: replaying is safe.

Recovery options compared

Scenario Best option
Accidental delete, backup from today Restore backup, replay the gap
Bad data for weeks Rebuild from source of truth
Region outage Restore a backup in another region (if supported for your setup) or rebuild from source there
One namespace damaged Restore to a new index, then copy that namespace's records back (fetch/upsert by ID list), or switch only that tenant's routing

Prevent it next time

  • Scheduled backups on every production index, with retention that matches your recovery window.
  • On-demand backup as a mandatory step in change runbooks for re-embedding and bulk deletes.
  • Deletion protection enabled on production indexes.
  • A delete helper that refuses an empty namespace and logs every bulk delete.
  • A restore drill each quarter, timed end to end.