🧹

Difyのスタジオ・ナレッジ・アカウントを、PostgreSQL操作とWSL+Shellcriptで一括削除した話

に公開
2026/01/09

はじめに

弊社では、生成AIアプリケーションをノーコードで作成できる「Dify」の研修を実施しています。
研修では、IT未経験からでも始められるようなレベルで、ハンズオンをメインに、議事録生成アプリや、Web検索と組織内部のデータを基に回答するようなチャットボット等いろいろ一緒に作っています。

ハンズオンを行っていると、研修後に発生するのが使用したDify環境のお掃除です。
アカウントやら、受講者の方に作成していただいたアプリや、アップロードされたファイル情報を削除して、次の研修に向けて環境をクリーンアップします。
これが結構面倒だったので、少しでも自動化したいと思うに至りました。

この記事は、Dify環境をクリーンにしたい同士に送る、PostgreSQL操作やShellScriptファイル化までの奮闘記となります。

Difyの前提知識

記事を読み進める前提知識として、Difyについて軽くご紹介します。
Dify(ディフィ)は、ノーコードで生成AIアプリを開発できるサービスで、画面クリックの操作で高度な処理ができるAIアプリが作成できてしまう手軽さが魅力になっています。
既存で用意された機能を組み合わせることで、様々な処理を組み合わせ、自分のタスクにカスタマイズしたアプリが作成できるのも強みです。

以下はDify公式サイトなので、気になる方は見てみてください!
https://dify.ai/jp

Difyの画面はこのようになっています。

この記事に出てくるDifyのパーツもご紹介します。

  • スタジオ
    アプリケーションを作成・編集するスペースで、作成したアプリケーションが管理される場所です。
    上の画像はスタジオを表示していますが、ここに表示された1つ1つの四角がアプリになります。
  • ナレッジ
    情報やドキュメントを事前に登録する場所です。自分しか持っていないような、独自の情報からチャットボット等に回答を生成してもらうときに使用します。
    Difyの画面では、下のようにナレッジのページで管理されています。
  • アカウント
    Difyにアクセスするためのアカウントです。
    下の画像は1アカウントしかありませんが、メンバーのページに登録されたアカウントがずらりと並びます。

Dify研修で出てきた問題

受講者の方と一緒に「スタジオ」でアプリを作ったり、参照させたい組織内部のダミーデータを格納する「ナレッジ」を作成してもらうと、研修が終わるたびに環境を初期化する作業が発生します。
つまり、

  • 使用済みアカウントの削除
  • 作成されたスタジオの総削除
  • 作成されたナレッジの総削除
    が必要になります。

これが、思った以上に面倒でした。

受講者1人につき少なくとも3つ、最大5つほどスタジオを作成してもらうため、10人の参加者でも最低30スタジオ削除しなければなりませんし、アカウントも受講者の人数分削除しないといけない。
これを画面で「削除」ボタンを押しながら1つずつやっていくのは、骨が折れるしやりたくない…。
かといってgitから新しく環境をpullして刷新すると、登録していたツールやLLMのAPIキーまで消えてしまって再登録が面倒…。

消したい情報である、スタジオ・ナレッジ・アカウントだけを一括で消すにはどうすればいいか、
これが研修で出てきた問題の1つでした。

使用しているDify環境、プラン

スタジオ・ナレッジ・アカウントだけを一括で消す作業をしたときの、使用していたDifyのプランはこちらです。
サービス : セルフホストサービス
プラン : Communityプラン
Dify version : 1.10.0

WSL上で、削除コードを書いてみた

スタジオの情報やアカウント情報といった、削除対象にしている情報はDBに保存されていると思われます。docker-compose.yamlファイルでコンテナ構成を見てみると、「db」というservice名が発見できます。DifyのDBは、PostgreSQLを使用しているのが分かります。

db:
    image: postgres:15-alpine
    restart: always
    environment:
      POSTGRES_USER: ${POSTGRES_USER:○○}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:○○}
      POSTGRES_DB: ${POSTGRES_DB:○○}
      PGDATA: ${PGDATA:-/var/lib/postgresql/data/pgdata}
    command: >
      postgres -c 'max_connections=${POSTGRES_MAX_CONNECTIONS:-100}'
               -c 'shared_buffers=${POSTGRES_SHARED_BUFFERS:-128MB}'
               -c 'work_mem=${POSTGRES_WORK_MEM:-4MB}'
               -c 'maintenance_work_mem=${POSTGRES_MAINTENANCE_WORK_MEM:-64MB}'
               -c 'effective_cache_size=${POSTGRES_EFFECTIVE_CACHE_SIZE:-4096MB}'
               -c 'statement_timeout=${POSTGRES_STATEMENT_TIMEOUT:-0}'
               -c 'idle_in_transaction_session_timeout=${POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT:-0}'

DBコンテナが分かったので、このコンテナにアクセスし、中のテーブルを覗きに行きます。

# DBコンテナに入り、PostgreSQLにアクセスする
docker exec -it{dbサービスが動いているコンテナ名}psql -U {ユーザー名} -d {DB名}

# コンテナ名は、docker psで確認できます。
# ユーザー名とDB名は上のComposeファイルのPOSTGRES_USER、POSTGRES_DBで確認できます 

スタジオの削除

まずはスタジオの情報がどこにあるか探してみます。
DB内を見たり、検索してみると、どうやらappsテーブルにスタジオ(作成したアプリ)情報が入っていました。

スタジオを消去する前に、PostgreSQLのDB情報をバックアップしたい場合は、以下のコマンドをWSL上で打ちます。

docker exec {dbサービスが動いているコンテナ名} pg_dump -U {ユーザー名} {DB名} > dify_backup_$(date +%Y%m%d).sql 

早速、スタジオから消していきます。PostgreSQL内で、以下のコマンドを打ちます。
一括削除のコマンドを打っていますが、appsのレコードが消えたかどうか、select文で確認しています。
この値が0であれば、消えています。

BEGIN;
TRUNCATE TABLE apps CASCADE;
SELECT COUNT(*) FROM apps; # 確認(件数チェックなど)
COMMIT;

この後にDify画面を見に行くと、スタジオが全て消えたのが確認できます。
これでスタジオは倒せたかと思いきや、実はスタジオのアプリ上にアップロードしたファイルや画像は消せていません…!
なぜなら、composeファイルにvolumeが設定されており、アプリ内でやり取りされたデータがホスト側にバインドされているからです。

volumes:
      # Mount the storage directory to the container, for storing user files.
      - ./volumes/app/storage:/app/api/storage # ここ!!

なので、ホスト側の情報も削除しに行きます。wslに移動し以下のコマンドを打ちます。

# difyディレクトリの、dockerフォルダのパスに移動
cd dify/docker/volumes/app/storage
# image_files, upload_files, toolsを削除
rm -rf image_files
rm -rf upload_files
rm -rf tools

これでスタジオは削除終了です。

ナレッジの削除

次に、ナレッジを削除してみましょう。
まずは、DBコンテナに入り、コンテナ内でPostgreSQLにアクセスしてナレッジを削除します。

# DBコンテナに入り、PostgreSQLにアクセスする
docker exec -it{dbサービスが動いているコンテナ名}psql -U {ユーザー名} -d {DB名}
# ナレッジ関係のレコードを削除
truncate table datasets cascade;
truncate table dataset_queries cascade;
truncate table dataset_process_rules cascade;

これでナレッジは消えますが、またしても注意が必要です。
RAGでナレッジをベクトル検索して使用している場合、ベクトルデータが残ってしまうからです。
weaviateをベクトルデータベースとして使用しているので、ここではweaviate上のベクトル情報を消しに行きます。
(wsl上で削除しますが、コンテナに入って削除していただいても構いません。)

cd dify/docker/volumes/weaviate
sudo rm -rf vector_index_* # wsl上では、sudoのパスワードが必要になります
# コンテナ上での削除は、Shellscriptファイルのコードをご参照ください。

これでナレッジの削除も終了です、ふぅ。

アカウントの削除

最後に、アカウントを削除していきます。アカウントで扱うテーブルは、「tenant_account_joins」と「accounts」テーブルです。

dbコンテナに入ったら、以下のコマンドを実行します。

# 自分以外のアカウントを削除する
delete from tenant_account_joins where role <> 'owner' 

BEGIN;
DELETE FROM accounts a
WHERE NOT EXISTS (
SELECT 1
FROM tenant_account_joins taj
WHERE taj.account_id = a.id
AND taj.role = 'owner'
);
COMMIT;

アカウント削除はこれで終わりです!
これで全部消せました!!
コンテナ内のメモリ上の不整合を解消するために、コンテナのリスタートだけ済ませ、完了です。

docker compose restart

最後に、ここでやったことをスクリプトファイル化してみます。

削除コードをShellscript化してみた

WSL上でやっていた上のコードを、スクリプトファイルにまとめたものが下になります。
パスはご自身の配置にあったものに置き換えていただければと思います。

#!/bin/bash -ex

# バックアップの作成が必要であれば
docker exec {dbコンテナ名} pg_dump -U {ユーザー名} {DB名} > dify_backup_$(date +%Y%m%d).sql

# SQLデータベースコンテナにアクセスし、スタジオを消去
docker exec -i {dbコンテナ名} psql -U {ユーザー名} -d {DB名} << 'SQL'
Truncate table apps cascade;
select count(*) from apps;
SQL

# 削除操作行うコンテナ
CONTAINERS=(
    "docker-api-1"
    "docker-weaviate-1"
)

# 削除対象ファイルパス(コンテナ側)
TARGETS=(
    "/app/api/storage/upload_files"
    "/app/api/storage/image_files"
    "/app/api/storage/tools"
)

for container in "${CONTAINERS[@]}"; do
    # ストレージ内のファイル削除
    if [ "$container" = "docker-api-1" ]; then
        for target in "${TARGETS[@]}"; do
            if docker exec "$container" sh -c "[ -d '$target' ]"; then
                docker exec "$container" sh -c "find '$target' -mindepth 1 -delete"
            fi
        done
    # ベクター情報の削除
    elif [ "$container" = "docker-weaviate-1" ]; then
        if docker exec "$container" sh -c '[ -d /var/lib/weaviate ]'; then
            docker exec "$container" sh -c "rm -rf /var/lib/weaviate/vector_index_*"
        fi
    fi
done

# ナレッジの削除
docker exec -i docker-db-1 psql -U postgres -d dify << 'SQL'
truncate table datasets cascade;
truncate table dataset_queries cascade;
truncate table dataset_process_rules cascade;
SQL

# アカウントの削除
docker exec -i docker-db-1 psql -U postgres -d dify << 'SQL'
delete from tenant_account_joins where role <> 'owner';
delete from accounts a
where not exists(
    select 1
    from tenant_account_joins taj
    where taj.account_id = a.id
        and taj.role = 'owner'
);
SQL

# コンテナの再起動
docker compose restart

このファイルに実行権限を付けます。
以下のコマンドを実行し、スクリプトファイルを動かしてみると、無事スタジオ・ナレッジ・アカウントが一括削除できていることが確認できました。

# スクリプトファイルに実行権限を付与する
# ファイルのあるディレクトリで実行すること
chmod +X {スクリプトファイル名}
./{スクリプトファイル名} 

まとめ

道のりは長かったですが、SQL操作やLinuxでの操作の練習になり、理解が深まって良かったです。
環境クリアがスクリプトファイルの実行でできるようになると、ストレスも減って良いです!

同じようにDify環境を綺麗にしたい人や、DifyのDB周りで悩んでいる方の参考になれば嬉しいです。

(弊社のDify研修に興味がございましたら、こちらからお申込みできますので、のぞいてみてください↓)
https://www.idnet.co.jp/service/difykenshu.html

株式会社インフォメーション・ディベロプメント

Discussion