Skip to main content

update

Update self-hosted Dify — pre-flight (Weaviate migration gate), back up volumes with the stack down, merge a release tag into dev, sync env, pull images, verify Arguments: [tag-or-version|main] [--yes] [--weaviate-staged].

Source facts

Repository
Agents-Store/codex-plugins
Last source activity
November 1, 2026 at 02:46
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
update
description
Update self-hosted Dify — pre-flight (Weaviate migration gate), back up volumes with the stack down, merge a release tag into dev, sync env, pull images, verify Arguments: [tag-or-version|main] [--yes] [--weaviate-staged].
# Dify Update Update a self-hosted Dify instance without losing data: read what the target release changes, stop the stack and archive `volumes/`, and only then merge the release tag into the local `dev` branch, sync environment variables, pull images and start. Nothing is changed before the plan in Step 3 is printed and confirmed. The branch model, the backup rules and the rollback this command follows are written down in `${CLAUDE_PLUGIN_ROOT}/skills/update-workflow/SKILL.md`; read it before Step 1. The Bash tool does not keep shell variables between calls. Step 2 prints `DIFY_ROOT`, `DOCKER_DIR`, `CUR`, `TGT`, `TARGET`, `TARGET_REF` and `STAGED`; carry them into the next block as plain assignments. Step 4 writes everything later steps need to `<backup-dir>/state.env` (paths, branch names, commit ids and the compose project — no secrets), and every block from Step 5 on starts with `set -a; . <backup-dir>/state.env; set +a`. ## Arguments `$ARGUMENTS` — an optional target plus flags: - a release tag such as `1.17.1`. Dify tags carry no `v` prefix; GitHub release titles read `v1.17.1`, so a leading `v` is dropped - `main` — merge `origin/main`. Its deployment files track development and can drift from the latest release; use it only when asked - empty — the latest stable tag (`MAJOR.MINOR.PATCH`). `rc`, `alpha` and `beta` tags are never picked - `--yes` — apply right after printing the plan, without the confirmation question. It never skips the Weaviate STOP, the dirty-tree question or conflict resolution - `--weaviate-staged` — you already stepped the bundled Weaviate volume to `1.39.2` with the official runbook (Step 2, check 5). It lifts that one STOP and nothing else ## Step 1: Detect Working Directory Determine whether the user is in `dify/` or in `dify/docker/`. ```bash if [ -f "docker-compose.yaml" ] && [ -f ".env.example" ]; then DOCKER_DIR="$(pwd)" DIFY_ROOT="$(dirname "$(pwd)")" elif [ -d "docker" ] && [ -f "docker/docker-compose.yaml" ]; then DOCKER_DIR="$(pwd)/docker" DIFY_ROOT="$(pwd)" else echo "ERROR: Cannot find Dify Docker setup." echo "Run this from dify/ or dify/docker/ directory." exit 1 fi echo "DIFY_ROOT=$DIFY_ROOT" echo "DOCKER_DIR=$DOCKER_DIR" ``` ## Step 2: Pre-flight (read-only) Five checks. None of them changes the working tree, the containers or the volumes. **1. Git state.** Run from `DIFY_ROOT`: ```bash cd "$DIFY_ROOT" git rev-parse --git-dir >/dev/null 2>&1 || { echo "ERROR: Not a git repo"; exit 1; } START_BRANCH=$(git branch --show-current) # empty = detached HEAD START_COMMIT=$(git rev-parse HEAD) DEV_TIP=$(git rev-parse -q --verify refs/heads/dev || echo "$START_COMMIT") # dev's tip before the merge; dev is created from HEAD when absent echo "Branch: ${START_BRANCH:-detached HEAD} Commit: $START_COMMIT dev tip: $DEV_TIP" git show-ref --verify --quiet refs/heads/dev && echo "dev branch: present" || echo "dev branch: MISSING" DIRTY=$(git status --porcelain) [ -n "$DIRTY" ] && { echo "WARNING: Uncommitted changes:"; git status --short; } ``` - Dirty tree — ask: (1) commit first, (2) stash (`git stash`, run in Step 5 right before the merge; record `STASHED=true`), (3) abort. - `dev` missing — the official `git clone --branch <tag>` leaves a detached HEAD and no `dev`. Tell the user Step 5 will run `git switch -c dev` before merging. - The rollback resets `dev` to `DEV_TIP`, never to `HEAD`: when the update starts on `main` or on a detached HEAD, `HEAD` is not where `dev` is, and resetting `dev` to it would throw away the user's customization commits. It then returns to `START_BRANCH` (or `START_COMMIT`). **2. Docker Compose >= 2.24.0** (the compose files use `env_file … required: false`): ```bash CV=$(docker compose version --short 2>/dev/null | sed 's/^v//') [ "$(printf '%s\n' "$CV" 2.24.0 | sort -V | head -1)" = 2.24.0 ] || { echo "ERROR: Docker Compose >= 2.24.0 required (found: ${CV:-none})"; exit 1; } ``` **3. Docker project name** — read it from the labels of the running `api` container of this directory, never parse container names: ```bash cd "$DOCKER_DIR" PROJECT_NAME=$(docker ps --filter "label=com.docker.compose.service=api" \ --filter "label=com.docker.compose.project.working_dir=$DOCKER_DIR" \ --format '{{.Label "com.docker.compose.project"}}' 2>/dev/null | head -1) echo "Docker project: ${PROJECT_NAME:-none running — compose default (directory name, usually docker)}" ``` If `PROJECT_NAME` is set, every `docker compose` call must run with `export COMPOSE_PROJECT_NAME="$PROJECT_NAME"`; without it a compose command in a block of its own addresses the default project (the directory name), and `down` stops nothing. Step 4 re-detects the project, exports it and saves it in `state.env`. **4. Target and versions.** Resolve the tag, then read the Dify version from the compose file of the target and of the current checkout: ```bash cd "$DIFY_ROOT" git fetch origin --tags # moves remote-tracking refs and tags, never the working tree STABLE='^[0-9]+\.[0-9]+\.[0-9]+$' REQ=""; YES=no; STAGED=no # parsed from the arguments; an empty REQ means the latest stable tag for a in $ARGUMENTS; do case "$a" in --yes) YES=yes ;; --weaviate-staged) STAGED=yes ;; *) REQ="$a" ;; esac done case "$REQ" in "") TARGET=$(git tag --list | grep -E "$STABLE" | sort -V | tail -1); TARGET_REF="$TARGET" ;; main) TARGET=main; TARGET_REF=origin/main ;; *) TARGET="${REQ#v}"; TARGET_REF="$TARGET" ;; esac git rev-parse -q --verify "$TARGET_REF^{commit}" >/dev/null || { echo "ERROR: '$TARGET_REF' not found. Latest stable tags:" git tag --list | grep -E "$STABLE" | sort -V | tail -5 exit 1; } TGT=$(git show "$TARGET_REF:docker/docker-compose.yaml" | grep -m1 -oE 'langgenius/dify-api:[0-9][^ ]*' | cut -d: -f2) CUR=$(grep -m1 -oE 'langgenius/dify-api:[0-9][^ ]*' "$DOCKER_DIR/docker-compose.yaml" | cut -d: -f2) echo "Current: ${CUR:-unknown} Target: $TARGET (images $TGT)" ``` An explicit pre-release tag is allowed with a warning that it is not for production. **5. Weaviate migration gate.** From Dify 1.17.1 the bundled Weaviate server moves from `1.27.0` to `1.39.2`. An existing data volume cannot cross 12 minor versions in one step; pulling and restarting silently and permanently breaks vector search. The gate fires only for a bundled Weaviate that holds data, an update that crosses `1.17.1`, and no `--weaviate-staged`. A fresh install, an external Weaviate and every other `VECTOR_STORE` are not affected. ```bash cd "$DOCKER_DIR" VS=$(grep -E '^VECTOR_STORE=' .env 2>/dev/null | tail -1 | cut -d= -f2); VS=${VS:-weaviate} WV_DATA=no if [ -d volumes/weaviate ] && { [ -n "$(ls -A volumes/weaviate 2>/dev/null)" ] || [ ! -r volumes/weaviate ]; }; then WV_DATA=yes; fi older() { [ "$(printf '%s\n' "$1" "$2" | sort -V | head -1)" != "$2" ]; } # true when $1 < $2; an empty $1 counts as older # CUR, TGT and STAGED come from check 4 if [ "$VS" = weaviate ] && [ "$WV_DATA" = yes ] && [ "$STAGED" != yes ] \ && older "$CUR" 1.17.1 && { [ -z "$TGT" ] || ! older "$TGT" 1.17.1; }; then echo "STOP: this update crosses Dify 1.17.1 and the bundled Weaviate moves 1.27.0 -> 1.39.2." echo "An existing volume cannot jump 12 minor versions; a plain pull + up -d breaks vector search silently." echo "Nothing was changed. Runbook: https://docs.dify.ai/en/self-host/deploy/troubleshooting/weaviate-server-migration-path" exit 1 fi echo "Weaviate gate: pass (VECTOR_STORE=$VS, volumes/weaviate data: $WV_DATA)" ``` On STOP do not run Step 4 or later: no merge, no `docker compose up -d`. Hand the user the runbook (summary in the `update-workflow` skill: back up the volume, step through every minor `1.27` → `1.38`, land exactly on `1.39.2`, always stop with `docker compose stop -t -1 weaviate`, never `docker kill` or `rm -f`), then offer three ways forward and ask which one: 1. Follow the runbook, confirm with `/v1/meta` that the server reports `1.39.2`, then run `/dify-ops:update <tag> --weaviate-staged`. 2. Update only to a release below `1.17.1` (for example `1.17.0`); the gate does not fire there. 3. Move to another vector store or an external Weaviate first (re-index the knowledge bases), then update. If `CUR` or `TGT` could not be read the gate fails closed and stops the same way. **Release notes for every step.** For each stable tag after `CUR` up to the target, show only the three sections that matter: ```bash cd "$DIFY_ROOT" git tag --list | grep -E "$STABLE" | sort -V | awk -v lo="$CUR" -v hi="$TARGET" 'p{print} $0==lo{p=1} $0==hi{exit}' | while read -r T; do echo "=== $T ===" gh release view "$T" -R langgenius/dify --json body -q .body 2>/dev/null \ | awk '/^## (Environment Variable Changes|Database Migrations|Upgrade Guide)/{p=1} /^## /&&!/^## (Environment Variable Changes|Database Migrations|Upgrade Guide)/{p=0} p' done ``` Without `gh`, give the links `https://github.com/langgenius/dify/releases/tag/<tag>` instead. Summarise renamed or removed variables, changed defaults, new services, slow or irreversible migrations; the upgrade steps depend on the release. ## Step 3: Plan (dry run) Print the plan with all eight blocks, then stop and ask "Apply this plan?" unless `--yes` was given. ```text DIFY UPDATE PLAN <CUR> -> <TARGET> TARGET <DIFY_ROOT> · compose project <PROJECT_NAME> · merge <TARGET_REF> into dev PRECHECK tree clean · Compose >= 2.24.0 · Weaviate gate: pass | n/a (<VECTOR_STORE>) · release notes read CHANGE git merge <TARGET_REF> · env sync (.env, envs/) · docker compose pull && docker compose up -d BACKUP <backup-dir>: docker-compose.yaml, .env, volumes.tgz — the whole stack is down while volumes/ is archived IMPACT downtime from down to up · start-up runs DB migrations, which are one-way · <changed env defaults, new services> VALIDATE docker compose ps · HTTP check · test retrieval in one knowledge base · model providers list ROLLBACK restore <backup-dir>, put dev back at <DEV_TIP>, return to <START_BRANCH> (command below) APPLY --yes, or the user's explicit go ``` Print the exact ROLLBACK command with the real values filled in — one runnable line, because git alone cannot undo a database migration: ```bash export COMPOSE_PROJECT_NAME=<project> && cd <DOCKER_DIR> && docker compose down && git -C <DIFY_ROOT> checkout -f dev && git -C <DIFY_ROOT> reset --hard <dev-tip> && git -C <DIFY_ROOT> checkout <start-branch-or-commit> && cp -p <backup-dir>/.env .env && sudo mv volumes "volumes.failed-$(date +%s)" && mkdir volumes && sudo tar -xzpf <backup-dir>/volumes.tgz -C volumes && docker compose up -d ``` `<project>` is the compose project from Step 2 (the directory name when none was running). `<dev-tip>` is `DEV_TIP`, the commit `dev` pointed at before the merge. `<start-branch-or-commit>` is `START_BRANCH`, or `START_COMMIT` when the update started on a detached HEAD. `<backup-dir>` is the directory Step 4 creates. Drop `sudo` when running as root; if the update was stashed, run `git stash pop` afterwards. ## Step 4: Backup, stack down Order matters: stop, archive, and only then change anything. The archive is taken from stopped containers, so postgres and Weaviate files are consistent. ```bash cd "$DOCKER_DIR" umask 077 # the archive holds the database and the storage key SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO=sudo TARGET="<TARGET>"; TARGET_REF="<TARGET_REF>" # from Step 2 # git state for the rollback — nothing has touched git yet START_BRANCH=$(git -C "$DIFY_ROOT" branch --show-current) START_COMMIT=$(git -C "$DIFY_ROOT" rev-parse HEAD) DEV_TIP=$(git -C "$DIFY_ROOT" rev-parse -q --verify refs/heads/dev || echo "$START_COMMIT") # compose project: detect it while the stack is still up, then pin it for every docker compose call below PROJECT_NAME=$(docker ps --filter "label=com.docker.compose.service=api" \ --filter "label=com.docker.compose.project.working_dir=$DOCKER_DIR" \ --format '{{.Label "com.docker.compose.project"}}' 2>/dev/null | head -1) [ -n "$PROJECT_NAME" ] && export COMPOSE_PROJECT_NAME="$PROJECT_NAME" VS=$(grep -E '^VECTOR_STORE=' .env 2>/dev/null | tail -1 | cut -d= -f2); VS=${VS:-weaviate} BACKUP_DIR="${DIFY_BACKUP_DIR:-$(dirname "$DIFY_ROOT")/dify-backups}/$(date +%Y%m%d-%H%M%S)" # outside the git repository
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub