Home / AI & GenAI / Vector Databases

AI & GenAIRunbookIntermediate

Upgrading Chroma without losing your collections: version pinning, backups, compatibility checks and the export-import fallback

A routine pip install --upgrade moved Chroma across a major version and the app now fails to open its data or talks to the server with the wrong API. Plan Chroma upgrades like database upgrades - pin versions, back up, test on a copy, upgrade client and server together, and keep an export-import path when the on-disk format changes.

Deepak Bhardwaj5 min read

Applies toChroma 0.4-1.x, embedded (PersistentClient) and server (Docker chromadb/chroma) deployments, Python client

Why Chroma upgrades break things

Chroma has moved fast. Across versions it has changed:

  • The on-disk storage format (for example, the move from DuckDB/Parquet to SQLite in 0.4, which needed a migration tool).
  • The HTTP API version (v1 to v2 endpoints) and client/server compatibility.
  • Configuration syntax (hnsw:* metadata keys to configuration objects in 1.x).
  • Return types (list_collections() returning names instead of objects in 0.6).
  • The server implementation (the core rewritten in Rust for 1.0).

Most of these come with migration notes and automatic migrations, but an unplanned upgrade in a production container can still leave an app that can't start. Treat Chroma like any other database.

Step 1: pin versions now

# requirements.txt
chromadb==<exact version>
# docker-compose.yml
image: chromadb/chroma:<exact version>

Pin both the Python package and the server image, and upgrade them together.

Step 2: read the release notes for every version you cross

Going from 0.5.x to 1.x crosses several releases. For each, look for:

Note type Impact
Storage migration Automatic on first open? Irreversible? Needs a tool?
API changes Client code changes (renamed parameters, return types)
Client/server compatibility Must upgrade both at once?
Configuration changes Collection settings syntax, env variables, data paths in the Docker image
Removed features Built-in auth providers, deprecated endpoints

Write the list of required code changes before touching production.

Step 3: back up

Stop writers, then copy the data directory:

# server in Docker
docker compose stop chroma
docker run --rm -v <project>_chroma-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/chroma-pre-upgrade-$(date +%F).tgz -C /data .
docker compose start chroma

# embedded
tar czf chroma-pre-upgrade-$(date +%F).tgz -C /app/chroma .

Also export counts per collection as a baseline:

import chromadb, json
client = chromadb.HttpClient(host="chroma", port=8000)
baseline = {}
for c in client.list_collections():
    name = c if isinstance(c, str) else c.name
    baseline[name] = client.get_collection(name).count()
json.dump(baseline, open("baseline-counts.json", "w"), indent=1)

Step 4: test the upgrade on a copy

  1. Restore the backup into a new directory or volume.
  2. Start the new server version (or open with the new package) against the copy.
  3. Run:
client = chromadb.HttpClient(host="localhost", port=8001)     # test instance
for name, n in baseline.items():
    col = client.get_collection(name)
    assert col.count() == n, (name, col.count(), n)
    col.query(query_embeddings=[sample_vec[name]], n_results=3)
print("ok")
  1. Run your application's retrieval evaluation set against the test instance and compare with the old version.
  2. Note how long the first open takes; automatic migrations on large data can take minutes.

If the copy fails to open or counts differ, stop and use the fallback (Step 7).

Step 5: upgrade production

Order Action
1 Announce a short write freeze; pause ingestion
2 Final backup
3 Deploy new server image
4 Wait for health (/api/v2/heartbeat on 1.x) and verify counts
5 Deploy app with the new client version and code changes
6 Run smoke queries and the evaluation set
7 Resume ingestion

For embedded deployments, steps 3 and 5 are the same deploy.

Step 6: rollback

A storage migration may be one-way. Rolling back means:

  • Restore the pre-upgrade backup to the data path.
  • Deploy the old server image and old client.
  • Replay any writes made after the upgrade from your ingestion log.

Keep the backup until the new version has run for at least a week.

Step 7: fallback - export and import

When an in-place migration isn't possible or fails, move the data through the API with both versions running side by side:

old = chromadb.HttpClient(host="chroma-old", port=8000)    # old version server on old data
new = chromadb.HttpClient(host="chroma-new", port=8000)    # new version server, empty volume

for c in old.list_collections():
    name = c if isinstance(c, str) else c.name
    src = old.get_collection(name)
    dst = new.create_collection(name, configuration={"hnsw": {"space": SPACE[name]}})
    offset = 0
    while True:
        b = src.get(include=["embeddings", "documents", "metadatas"], limit=1000, offset=offset)
        if not b["ids"]:
            break
        dst.add(ids=b["ids"], embeddings=b["embeddings"], documents=b["documents"], metadatas=b["metadatas"])
        offset += 1000
    print(name, src.count(), dst.count())

Client versions: one Python environment can only have one chromadb version installed. Run the export with the old client version to a neutral format (JSON Lines or Parquet with ids, embeddings, documents, metadatas), then import with the new version from a separate environment. That also gives you a portable backup.

import json
with open(f"{name}.jsonl", "w") as f:
    for i, e, d, m in zip(b["ids"], b["embeddings"], b["documents"], b["metadatas"]):
        f.write(json.dumps({"id": i, "embedding": list(map(float, e)), "document": d, "metadata": m}) + "\n")

Checklist

Item Done when
Versions pinned Package and image pinned and upgraded together
Release notes Read for every crossed version; code changes listed
Backup Data directory archived; baseline counts saved
Test Copy opened with new version; counts and quality match
Rollback Backup kept; old images available

Prevent it next time

  • Pin Chroma like a database version, never chromadb>=… in production.
  • Keep a JSONL/Parquet export of every collection as a version-independent backup.
  • Script collection creation (space, HNSW settings) so rebuilding on a new version is easy.
  • Upgrade one minor version at a time when the release notes mention storage changes.