//------------------------------------------------------------------- //-------------------------------------------------------------------
Upgrade Self-Hosted Dify

How to Upgrade Self-Hosted Dify Safely with Database Backups, Migration Checks, and Rollback

Upgrading a production Dify stack is not a simple git pull command; you need a real backup, a migration check, and a tested rollback path before you upgrade self-hosted Dify in production. This guide shows you how to safely upgrade a Dify installation to avoid data loss or corruption.

Why Dify Upgrades Fail Without Preparation

Dify’s docker-compose stack bundles 13 to 15 containers, including API, worker, worker_beat, web, plugin_daemon, Postgres, Redis, and a vector store. Every minor release can add new environment variables, new services, or database migrations. Skipping the backup and validation steps when you upgrade self-hosted Dify is how teams end up with a corrupted messages table, mismatched .env variables, or a database stuck mid-migration.

If you haven’t set up your Dify instance yet, check out our guide on self-hosting Dify with Docker Compose before continuing with this upgrade tutorial.

Prerequisites to Upgrade Self-Hosted Dify Safely

Before you upgrade self-hosted Dify, confirm you have shell access to the server, enough free disk space for a full volume backup, and a maintenance window since Dify has no zero-downtime upgrade path in Docker Compose mode.

  • Root or sudo access to the server hosting your Dify Docker setup.
  • Enough free disk space, at least double the size of your current volumes/ folder for backups.
  • Know your current Dify version tag before starting: git describe --tags or check docker compose ps image tags.
  • A maintenance window, since upgrading takes the app offline for all users.
  • The changelog for the version you’re upgrading to, reviewed in advance.

Step 1. Review Release Notes Before Choosing a Version

Never upgrade self-hosted Dify by jumping to main; always use a specific tagged release instead. Dify’s docs and community issues show that even minor version updates can bring breaking changes, like the Postgres hostname switching from db to db_postgres or new services becoming required.

cd ~/dify/docker
git fetch --tags
git tag -l | tail -20          # list recent stable tags
git describe --tags            # confirm the currently running version

Read the changelog for every version between your current one and the target, not just the latest release; migrations build on top of each other. Pinning to a specific version this way keeps you from accidentally running untested code.

Step 2. Back Up the PostgreSQL Database

Every safe attempt to upgrade self-hosted Dify starts with a full PostgreSQL dump, separate from the volumes backup, because a pg_dump can be restored even if the data directory itself gets corrupted during copy.

Create a new backup folder named with the current date and time:

mkdir -p ~/dify-backups/$(date +%Y%m%d_%H%M)

Navigate to the Dify Docker directory and export the Dify database from inside the Postgres container into a backup file:

cd ~/dify/docker
docker compose exec db_postgres pg_dump -U postgres -d dify \
-F c -f /tmp/dify_backup.dump

Then, copy that backup file out of the container onto your server, into the folder you just created:

docker compose cp db_postgres:/tmp/dify_backup.dump \
~/dify-backups/$(date +%Y%m%d_%H%M)/dify_backup.dump

Before moving on, check that the backup file isn’t empty:

pg_restore --list dify_backup.dump

Never copy Postgres’s live data files while the container is still running. Doing this instead of a proper dump has caused real migration failures, since the database’s internal logs can end up inconsistent.

Step 3. Back Up the Docker Volumes

The second backup layer needed before you upgrade self-hosted Dify is a full archive of docker/volumes/. This directory holds Postgres data, Redis state, uploaded files, and plugin storage; deleting it means deleting the entire install.

cd ~/dify/docker
docker compose down                       # stop containers, keep volumes intact
tar -czvf ~/dify-backups/volumes-$(date +%s).tar.gz volumes/
docker compose up -d                      # bring the current version back up while you verify the backup

Note: Use tar instead of zip for backups. Zip can break symlinks in the Python environment and plugin folders, which has caused migration errors before.

For extra safety, you can send a copy of this archive offsite to S3 or MinIO. This guide on backing up Docker volumes to S3 or MinIO shows the process. That way, you can still roll back even if the server itself goes down.

Step 4. Validate Environment Variable Changes

Mismatched .env files are the most common reason teams fail to upgrade self-hosted Dify, because new releases regularly add required variables that don’t exist in an older .env file.

cd ~/dify/docker
cp .env .env.$(date +%s).bak              # snapshot the current env before editing
diff .env.example .env | grep '^<'         # variables in .env.example missing from .env

Add any new variables the diff shows, and check for values that changed defaults. For example, past upgrades changed which user the container runs as, which meant a one-time chown fix was needed on the storage folder:

sudo chown -R 1001:1001 ./volumes/app/storage

Compare each changed variable with the release notes rather than copying .env.example over your file.

Step 5. Run the Dify Migration on a Staging Copy First

Before you upgrade self-hosted Dify in production, run through the same upgrade steps on a clone of the server or a spare VM with your restored backups. This way, if anything breaks during migration, it happens somewhere safe instead of in front of real users.

git checkout <target-tag>
cd docker
docker compose down
docker compose pull
docker compose up -d

If the version you’re upgrading to doesn’t run migrations automatically, you’ll need to run the database and plugin migration commands yourself, in this exact order:

docker compose exec api uv run flask db upgrade
docker compose exec api uv run flask extract-plugins --workers=20
docker compose exec api uv run flask install-plugins --workers=2
docker compose exec api uv run flask migrate-data-for-plugin

Recent releases run flask db upgrade automatically on API container start when MIGRATION_ENABLED=true is set, so check your .env for that flag before running migrations manually and risking a duplicate run.

Step 6. Watch the Containers Startup Logs

Watching the containers start up is a key part to upgrade self-hosted Dify safely. Compose starts services in a specific order:

  1. init_permissions runs first and exits.
  2. Postgres waits until it’s healthy.
  3. The API starts.
  4. Nginx comes up last.

List all containers with their name and current status:

docker compose ps --format "{{.Name}} {{.Status}}" | sort

Check the logs with:

docker compose logs -f api worker db_postgres

If any core service, including api, worker, or the database, is not marked healthy within a couple of minutes, stop before touching production traffic and check logs for migration errors, missing tables, or stuck indexes.

Step 7. Confirm Dify Apps Still Work After Upgrading

Just because containers start cleanly doesn’t mean your data survived correctly. You must test real workflows and plugins after upgrading, not just the login page.

  • Log in with an admin account and check that your apps and datasets are still there.
  • Open an existing chatflow or workflow and run it start to finish.
  • Check that your model provider plugins still have valid API keys.
  • Query the knowledge base to ensure it still connects to the vector store.
  • If something looks off, run docker compose exec api flask db history and compare it with api/migrations/versions/.

Roll Back to the Last Dify Version Without Losing Data

If tests fail, the safest way to roll back after a failed attempt to upgrade self-hosted Dify is to restore the previous volumes archive and database dump. To do this, you can use:

cd ~/dify/docker
docker compose down -v                     # stop and remove the failed upgrade's containers
git checkout <previous-tag>                # return to the last known-good tag
rm -rf volumes/
tar -xzvf ~/dify-backups/volumes-<timestamp>.tar.gz -C .
docker compose exec db_postgres pg_restore -U postgres -d dify \
  --clean ~/dify-backups/<timestamp>/dify_backup.dump
docker compose up -d

Note: Never restore volumes while containers are still running. Always stop the stack first, just like during the backup step. Restoring live risks the same kind of corruption that happens when Postgres files are copied without stopping the service.

Pre-Upgrade Checklist for Dify

This checklist is what actually prevents downtime when you upgrade self-hosted Dify, beyond just running git pull:

CheckWhy it matters
Target version tag confirmed, not mainAvoids untested commits
Changelog read for every version in betweenCatches renamed services, new required vars
pg_dump completed and verified restorableProtects against corrupted migrations
volumes/ archived with tar, not zipPreserves symlinks in plugin/venv paths
.env diffed against .env.examplePrevents missing-variable startup failures
Migration staged on a clone/spare VM firstIsolates failures from production
Maintenance window scheduledCompose upgrade requires full stack downtime
Disk space checked (2x current volumes size)Backup + new images need headroom

Dify Rollback Checklist

This rollback checklist assumes you already tried to upgrade self-hosted Dify and hit an error during migration, startup, or testing.

StepCommand/action
Stop the failed stackdocker compose down -v
Check out previous taggit checkout <previous-tag>
Restore volumes archivetar -xzvf volumes-<timestamp>.tar.gz -C .
Restore database dumppg_restore -U postgres -d dify --clean <dump>
Restart on old versiondocker compose up -d
Re-verify workflows and pluginsRepeat Step 7 checks above

Where to Run Production Dify

A safe rollback needs enough free disk space for backups and snapshots, so don’t run production Dify on a minimal server. Our dedicated server hosting plans give you dedicated NVMe storage built for this exact need, including enough room to keep both the old and new versions side by side during an upgrade, without worrying about running out of space.

With backups, tested migrations, and a working rollback plan in place, you can upgrade self-hosted Dify with confidence instead of just hoping nothing breaks.

Conclusion

Upgrading Dify safely is less about technical skill and more about discipline. You must pin your version, back up the database, archive the volumes, check the .env file, test the migration first, and verify workflows before you call it done. Follow this checklist every time, and upgrading turns from a risky task into something routine and reversible.

We hope you enjoy this guide. For version-specific changelogs and upgrade notes before every release, you can check the Dify official GitHub Releases page.

FAQs

Do I need to stop Dify before backing up volumes?

Yes, run docker compose down first, since copying live Postgres files can corrupt the database.

Can I skip the Dify database migration step?

No, recent versions require flask db upgrade and, for plugin-based releases, migrate-data-for-plugin, or the app will throw internal errors.

Is docker compose pull enough to upgrade Dify?

No, you must also git checkout the matching source tag first, since compose files and migrations are version-specific.

Post Your Comment

PerLod delivers high-performance hosting with real-time support and unmatched reliability.

Contact us

Payment methods

payment gateway
Perlod Logo
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.