1.27.0 to 1.39.2, and an existing Weaviate data volume cannot cross that gap in one step. This procedure covers Dify’s default, single-node Docker Compose deployment. Fresh deployments start with the new version on an empty volume and do not need this procedure.
Weaviate neither tests nor supports skipping minor versions, and any release may carry an on-disk migration that expects the previous version to have run at least once.
The supported path is to step through every minor release in order, landing on the latest patch of each. Below is that ladder, along with the two operational details that most often go wrong along the way.
Check Your Current Version
From the Dify repository root, enter thedocker directory and keep the shell there. The default docker-compose.yaml does not publish a host port for Weaviate, so run the check inside the Compose network rather than against localhost on your host:
localhost in the URL is the Weaviate container’s own loopback, not your host’s. The Weaviate image has no curl and no Python, so the request uses busybox wget and the JSON is formatted by Python on your host.
If this reports 1.39.2, you are done. Continue below from 1.27.0; if it reports an intermediate version in the ladder, resume with the next rung. For a version earlier than 1.27.0, follow the Weaviate v4 migration guide first.
Back Up Your Data
For a Docker deployment, the simplest backup is an offline copy of the volume. First stop Dify’s request and worker services, and keep them stopped until the final rung passes: If yourdocker-compose.yaml does not define api_websocket, omit it from the command below.
cp -a preserves ownership, permissions and timestamps. A plain cp -r run under sudo rewrites them, which can leave the restored volume unreadable by the Weaviate container.
In production, prefer Weaviate’s backup module writing to S3, GCS, or Azure. It produces a restorable snapshot rather than a raw directory copy, and it can run against a live instance. The local filesystem backend is for development only.
Upgrade One Minor at a Time
Move through every minor release in order, always landing on that minor’s latest patch.
The registry changes at step 1. Existing deployments pull
semitechnologies/weaviate from Docker Hub; the ladder uses cr.weaviate.io/semitechnologies/weaviate, Weaviate’s own registry, which is what Dify’s updated Compose file points at. The images are the same, so you are changing both registry and version as one deliberate move.
Patch versions were current at the time of writing. For the intermediate rungs, check for a newer patch of that minor before you start and prefer it.
Stepping one minor at a time also limits your blast radius. When a rung misbehaves, you roll back a single version and know exactly which release caused it, instead of bisecting a jump that crossed every minor at once.
Staying close to the newest release keeps you in the fix window. Weaviate supports the three most recent minor versions—the current one and the two before it. Anything older is end of life and has no claim on bug or security fixes. Revisit periodically after this migration and move to the current patch.
Upgrade with Docker Compose
Dify pins the Weaviate image indocker/docker-compose.yaml, docker/docker-compose.middleware.yaml, and docker/docker-compose-template.yaml. Edit the file you deploy with, then repeat the loop below for each rung.
1
Set the Next Image Tag
Change the
weaviate service to the next minor’s latest patch, for example image: cr.weaviate.io/semitechnologies/weaviate:1.28.16.2
Stop the Container Gracefully
docker kill or docker rm -f here. See Stop Weaviate Gracefully for what a hard kill costs you.3
Start the New Version
4
Verify Before Moving On
Run Verify Each Step before continuing.
docker-compose.middleware.yaml while developing from source, the same loop applies with the file named explicitly:
Stop Weaviate Gracefully
This is the single detail most likely to cost you working search, and it is easy to get wrong. Weaviate holds its HNSW vector index in memory and records it to disk through a commit log. When the container is hard-killed—docker kill, docker rm -f, an out-of-memory kill, or a stop that exceeds Docker’s grace period—that commit log can be left incomplete, and objects it had not yet recorded end up missing from the graph.
Your objects are not lost. The graph is. Testing this on 1.39.2, a hard kill immediately after importing 500 objects into a 700-object collection left it like this:
Twenty objects sat in the store, fully intact, and vector search could not reach them.
What makes this dangerous is that nothing announces it. The server logged no error, no warning, and nothing at all about the commit log—there is no line to grep for. And the object count, the check most people reach for first, reads perfectly healthy.
It also does not heal on its own. The same twenty stayed missing after the restart, after a graceful stop and start, after a second one, and after re-writing the same objects. To repair it, re-index the affected knowledge base in Dify so the collection is built again from scratch.
So prevention is the whole game here. Use
docker compose stop or docker compose down, both of which send SIGTERM, and let the process finish flushing. A timeout of -1 waits indefinitely instead of falling back to SIGKILL:
near_vector query with limit set to that number. Fewer results back means the difference is your unreachable objects.
Wait for the Index to Mount After Each Restart
/v1/meta starts answering about two seconds after the container starts, on every version on the ladder. Your collections take longer to mount, and from 1.31 onward they take much longer—measured across the whole ladder, the gap between /v1/meta answering and the first successful collection query is:
A query fired inside that window does not report “still starting”. It reports that your data is not there:
GET /v1/objects?class=...returns 404 with an empty body.- A GraphQL query returns 422 with
no graphql provider present, this is most likely because no schema is present. Import a schema first!
/v1/meta alone.
Verify Each Step
Confirm that data survived and that search works. A server that boots is not evidence of a healthy index. Set your key and collection name once, then run the four checks. Dify replaces every hyphen in the knowledge base ID with an underscore when it names the collection, so copy the exact name from the second check rather than building it by hand:1.39.2 passes, run docker compose stop -t -1 weaviate || exit 1. Revert only the temporary image edit without running Compose, then follow the Docker Compose upgrade instructions for Dify 1.17.1. Run a test retrieval before returning the deployment to service; getting chunks back confirms the vector index survived, which the object count alone does not tell you.
Roll Back a Failed Step
Before starting Dify 1.17.1, set the Weaviate image back tosemitechnologies/weaviate:1.27.0, then restore the initial backup:
What Does Not Change
Your collections keep their existing layout, so no re-indexing is required and your knowledge bases keep working as they are. Dify stores each dataset as aVector_index_<dataset_id>_Node collection, with the ID’s hyphens replaced by underscores, and a self-provided named vector default. Dify does not set a distance metric, so the collection uses the server default of cosine. That structure is identical before and after the upgrade.
The Python weaviate-client also needs no attention. Use the version your Dify release pins: 4.20.5 for Dify 1.14.0 through 1.16.1, and 4.22.0 for 1.17.0 through 1.17.1. Both work against every server version on the ladder, so you can upgrade the server independently of Dify.
Running a Weaviate server older than 1.27, or moving from client v3 to v4? Start with the Weaviate v4 migration guide, which covers 1.19.0 through 1.26.x and the schema migration up to 1.27, then return here.