Skip to main content
Dify 1.17.1 updates its bundled Weaviate server from 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.
Back up your Weaviate volume before you start. An upgrade that goes wrong partway through the ladder is only recoverable if you have a snapshot to return to.

Check Your Current Version

From the Dify repository root, enter the docker 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:
The 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 your docker-compose.yaml does not define api_websocket, omit it from the command below.
Run Verify Each Step once to record the starting object count and confirm synchronization. Then stop Weaviate and copy the volume:
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.
The final rung is the exception. Land on the version your docker-compose.yaml pins, not the newest patch available. If you take the volume to a newer patch and then apply Dify’s pin, you have downgraded onto a volume the newer binary already wrote to—the case the rollback warning covers.
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 in docker/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

Never use 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.
If you run Weaviate through 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:
To check whether it already happened, compare the object count against what vector search can actually reach. Verify Each Step gives you the count; run a 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!
That is alarming to read halfway through an upgrade, and it is a false alarm. The window is transient and needs no action beyond waiting. Give each restart ten seconds before you conclude anything, and if you script health checks, poll until a real collection query succeeds rather than trusting /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:
Continue only if the reported version matches the current rung, the object count is unchanged, and the synchronization check succeeds.
Do not use GET /v1/objects?class=...&limit=1 to count objects. Its totalResults field reports the size of the page it returned, not the size of the collection, so with limit=1 it always prints 1. Use the Aggregate query above.
After 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 to semitechnologies/weaviate:1.27.0, then restore the initial backup:
Rolling the image tag back without restoring the data is not safe. Once a newer version has written to the volume, an older binary may not read it.
Verify Weaviate, then start the original Dify stack and run a test retrieval.

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 a Vector_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.
Last modified on September 10, 2026