0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

n8nをバックアップから復旧したら `SQLITE_CORRUPT: orphan index` で起動ループした話

0
Posted at

TL;DR

  • 稼働中の n8n の .n8n ディレクトリを tar で固めたバックアップから復旧したら、SQLITE_CORRUPT: malformed database schema (sqlite_autoindex_binary_data_1) - orphan index で起動ループになった
  • .recover.dump も通らず、SQLiteの修復コマンドは全滅
  • 最終的に効いたのは修復ではなく、「壊れていない世代を探して入れ直す」ことだった
  • 破損DBの修復に時間を使う前に、find でDBファイルを全部洗い出すほうが速い

1. 背景:なぜ環境を作り直したのか

社内の自動化基盤として、余っていた Windows 11 Pro のミニPCで、n8n(Node.jsベース)と Skyvia Agent(.NETベース)の2つを常時稼働させていました。これが1週間ほど動かすと、メモリが詰まってフリーズするようになりました。

⚠️ 誤解のないように書いておくと、「メモリリークである」と計測して確かめたわけではありません。Windows本体のフットプリント、常駐する2つのランタイム、そして後述するSQLiteの肥大化が積み上がった結果、16GBが足りなくなっていた、というのが実態に近いです。

そこで、ツールを乗り換えるのではなく実行基盤を作り直す方針にしました。

[変更前] Windows 11 Pro
           ├─ n8n(Windows上のプロセス)
           └─ Skyvia Agent(Windows上のプロセス)
                  ↑ どちらかが暴れると全部巻き込んでフリーズ

[変更後] Ubuntu Server 24.04 LTS
           ├─ Docker ─ n8n
           └─ KVM ─ Windows 11 VM ─ Skyvia Agent
                  ↑ Windows側が固まっても、n8nは無傷。VMだけ再起動すればいい

Skyvia Agent をわざわざVMに閉じ込めているのは、Agent の Linux 版がまだ提供されていないためです(2026年8月時点で公式サイトのLinux/macOS版は "Coming soon")。影響範囲をVMに封じ込める方向で妥協しました。

本記事はこのうち n8n のデータ移行でハマった部分だけを扱います。KVM側のネットワーク(NAT配下のVMに外から繋ぐ話)は後編で扱います。


2. 事件の始まり:docker load が通らない

旧環境から持ち出したバックアップが n8n_backup.tar.gz という名前だったので、Dockerイメージのつもりで読み込もうとしました。gzipを解いても同じでした。

$ docker load -i ./n8n_backup.tar.gz
unrecognized image format

中身を見ます。

$ tar -tvf ./n8n_backup.tar | head
./database.sqlite
./database.sqlite-wal       ← WALファイル
./database.sqlite-shm       ← 共有メモリファイル
./config
./binaryData/
./nodes/package.json

Dockerイメージではなく、.n8n ディレクトリのバックアップでした。 docker load が読めるのは docker save の出力だけなので、当然です。

tarの中身 正体 使うコマンド
manifest.json、ハッシュ名のディレクトリ、layer.tar Dockerイメージ docker load -i
コンテナのファイルシステムそのもの docker export の出力 docker import
database.sqliteconfigbinaryData/ n8nのデータ 展開してバインドマウント

そして、この出力にはもう一つ重要な情報が写り込んでいます。-wal-shm が同梱されている=n8nを止めずにバックアップを取っているということです。これが後の破損に効いてきます。


3. compose の書き換えで踏んだ小さな穴

データを展開して起動しようとしたら、composeが弾いてきました。

$ docker compose up -d
service "n8n" refers to undefined volume n8n-data: invalid compose project

docker-compose.yml のYAMLが崩れており、named volume を参照しているのにトップレベルの volumes: 定義が壊れていました。

# NG
    volumes:
      - n8n-data:/home/node/.n8nvolumes:   # ← 改行が消えて結合している
  n8n-data:
    driver: local~                          # ← 余計な文字

今回はtarから展開したディレクトリをそのまま読ませたいので、named volume をやめてバインドマウントに統一します。あわせて所有権も合わせます。n8n公式イメージは node ユーザー(UID/GID 1000)で動くためです。

    volumes:
      - ./n8n_data:/home/node/.n8n    # ← 展開先ディレクトリを直接マウント
sudo chown -R 1000:1000 ./n8n_data

⚠️ chmod -R 775 を反射で打たないこと。 私はこの後うまくいかない焦りから打ってしまいましたが、必要だったのは chown だけでした。パーミッションの緩和は原因究明ではなく、原因を見えにくくする行為です。


4. 起動した。でもワークフローが1件も無い

コンテナは上がり、ブラウザで http://<HOST_IP>:5678 を開くと……アカウント新規登録画面が出ました。復旧できていれば旧環境のログイン画面が出るはずです。N8N_ENCRYPTION_KEY.env に入れて再起動しても変わりません。

コマンド履歴を見返して分かりました。

 69  git clone git@github.com:<ORG>/n8n-compose.git
 70  cd n8n-compose/
 71  docker compose up -d          # ← ここ!バックアップを展開する前に起動している
 ...
 84  tar -xvf ./n8n_backup.tar -C ~/restore/n8n_data/    # ← 本物はこっちに展開した

71行目で、リポジトリのcomposeがそのまま起動し、空のDBを自動生成していました。

~/restore/n8n_data/              ← 本物のバックアップ(84行目で展開)
~/work/n8n-compose/n8n_data/     ← 空のDB(71行目で自動生成)※composeが見ているのはこっち

n8nは「DBが無ければ黙って新規作成する」ので、間違ったディレクトリを見ていても、エラーではなく「まっさらな画面」として現れます。これが一番タチが悪いところでした。

💡 git clone 直後に何も考えず docker compose up -d しない。 バインドマウント先が空だと、その瞬間に初期化済みの状態が作られます。


5. 本命の事故:SQLITE_CORRUPT: orphan index

本物のデータを正しいディレクトリにコピーして起動したところ、今度はコンテナが起動ループに入りました。

n8n-1 | Failed to load Custom API options for the node "n8n-nodes-base.confluence": Unknown credential name "confluenceCloudOAuth2Api"
n8n-1 | Initial database connection attempt 1 failed: SQLITE_CORRUPT: malformed database schema (sqlite_autoindex_binary_data_1) - orphan index. Retrying in 1000ms
n8n-1 | (5回リトライして)There was an error initializing DB
n8n-1 |  --> in Database#run('PRAGMA journal_mode = WAL', [Function (anonymous)])
n8n-1 | n8n-1 exited with code 1 (restarting)

binary_data テーブルの自動インデックスが本体を失った「孤児インデックス」状態です。PRAGMA journal_mode = WAL を投げた瞬間にスキーマ読み込みが死ぬので、アプリからは手も足も出ません

原因は明確で、n8nを稼働させたまま .n8ntar で固め、さらに cp -r で上書きコピーしたためです。SQLiteはWALモードで動いており、database.sqlite 本体・-wal-shm の3ファイルが整合していないと壊れます。3つを別々のタイミングでコピーすれば、当然こうなります。

修復コマンドは全滅した

試したこと 結果
sqlite3 database.sqlite .recover sql error: no such table: sqlite_dbpage (1).recoversqlite_dbpage 仮想テーブルに依存するが、Ubuntu 24.04 標準の sqlite3 では使えない
sqlite3 database.sqlite .dump malformed database schema ... orphan index.dump も最初にスキーマ全体を読むので同じ場所で死ぬ
テーブルを指定して .dump 同上。テーブルを絞ってもスキーマ読み込みは全体に走る

唯一効いたのは strings での生サルベージ

SQLiteとしてではなく、ただのバイト列としてJSONを引っこ抜きます

sudo apt install -y binutils        # Command 'strings' not found と言われたらこれ
strings database.sqlite | grep -E '"nodes":\[.*\]' > recovered_raw.txt

取り出したJSONは、n8nのキャンバス上で Ctrl+V(Mac は Cmd+V)するだけでノード群として復元できます。ただしn8nが受け付けるのは nodesconnections を持つオブジェクトなので、拾った断片を1ワークフロー分ずつ {"nodes": [...], "connections": {...}} の形に整えてから貼ってください。connections が拾えなければ {} で構いません。ノードは全部並ぶので、線は引き直せます。

インポート機能を使わずに済むのが利点です。 CLIの export:workflow --all は、古いノードが1つ混ざっているだけで全件が出せなくなります(冒頭のログにある Unknown credential name がそれです)。import:workflow にも罠があり、

$ docker compose exec n8n n8n import:workflow --input=/home/node/.n8n/extracted_workflows.json
ENOENT: no such file or directory, open '/home/node/.n8n/extracted_workflows.json'

--output / --input に渡すのはコンテナ内のパスです。ホスト側の ~/work/n8n-compose/n8n_data/ に出力したファイルを ~ 付きで参照しても見つかりません。

結論:ノードのバージョン互換で詰まるなら、CLIよりブラウザUIの Import from File / キャンバスへの貼り付けのほうが強い。


6. 解決:修復しない。壊れていない世代を探す

.recover.dump も潰えた時点で、視点を変えました。

「そもそも、壊れているのはどのファイルなのか?」

場所 中身 状態
n8n_backup.tar 旧環境からのアーカイブ 無傷
~/restore/n8n_data/ tarを展開したもの 無傷
~/work/n8n-compose/n8n_data/ 上をコピー+起動+権限変更を繰り返した作業場 破損

つまり、壊れていたのは「コピー先」だけで、大元は生きていました。 破損DBの修復に費やしていた時間は、丸ごと無駄だったわけです。

cd ~/work/n8n-compose
docker compose down

# 汚染された作業ディレクトリを「消さずに」退避して、作り直す
mv n8n_data n8n_data.broken
mkdir n8n_data

# 無傷の展開物から入れ直す(chown だけでいい。chmod は不要)
cp -a ~/restore/n8n_data/. ./n8n_data/
sudo chown -R 1000:1000 ./n8n_data

docker compose up -d

ワークフローも Data tables も、旧環境のまま完全に復活しました。

この件の教訓

破損DBの修復コマンドを試す前に、find を打て。

find ~ -name 'database.sqlite*' -exec ls -lh {} \;

30秒で終わります。.recover のオプションを調べていた1時間より、こちらのほうがはるかに価値がありました。

同じ理由で、復旧作業は必ず「コピーの上」で行い、元バックアップには絶対に触らないこと。今回助かったのは、たまたま cp で作業していて mv していなかったからです。


7. 再発防止:正しいバックアップの取り方

今回の事故は、バックアップの取り方を間違えていたことに尽きます。

# ❌ n8n を動かしたまま固める(WAL/SHMが中途半端に混ざる)
tar czf n8n_backup.tar.gz -C ~/work/n8n-compose/n8n_data .
# ⭕ 止めてから取る(停止は数秒。cronで日次実行すれば十分)
cd ~/work/n8n-compose
docker compose stop n8n
tar czf "backup/n8n_$(date +%Y%m%d).tar.gz" -C ./n8n_data .
docker compose start n8n
ls -1t backup/n8n_*.tar.gz | tail -n +4 | xargs -r rm --   # 3世代より古いものを削除

止めたくないなら、SQLiteのオンラインバックアップAPIを使います。.backup は書き込み中でも整合性のあるスナップショットを取るので、ファイルコピーとは根本的に別物です。

sqlite3 ~/work/n8n-compose/n8n_data/database.sqlite ".backup '/path/to/backup.sqlite'"

N8N_ENCRYPTION_KEY は別の場所にも保管してください。 DBだけ戻しても、このキーが無いと Credentials(外部サービスの接続情報)が一切復号できません。.env と同じ場所にしか無いのでは、バックアップの意味が半減します。

そしてDBが肥大化しなければ事故の確率も下がるので、実行履歴の自動削除も入れておきます。常時稼働で履歴を貯め続ける前提なら、素直に PostgreSQL に移すべきです。今回の orphan index は、SQLiteのWAL構成をファイルコピーで扱ったがゆえの事故でした。

environment:
  - EXECUTIONS_DATA_PRUNE=true
  - EXECUTIONS_DATA_MAX_AGE=168     # 168時間 = 7日分だけ保持

8. まとめ

遭遇したエラー 真因 対処
unrecognized image format イメージではなくデータのtarだった tar -tvf で中身を見てから判断
refers to undefined volume YAML崩れ+named volumeの定義漏れ バインドマウントに統一
ワークフローが0件 n8n_data が2箇所にあり、空のほうを見ていた composeのマウント先を確認
SQLITE_CORRUPT: orphan index 稼働中の .n8n をファイルコピーした 修復せず、無傷の世代から入れ直す
no such table: sqlite_dbpage .recover 非対応ビルド strings で生サルベージ
Unknown credential name 古いノードでCLIエクスポートが全滅 ブラウザUIから読み込む
ENOENT ホストパスとコンテナ内パスの混同 マウントの対応関係を確認

一番言いたいのはこれです。

修復は最後の手段。まず「壊れていないコピー」を探す。

後編では、このサーバのKVM上に建てた Windows VM に外からRDPしようとして踏んだネットワークの地雷を書きます。ping が 100% loss なのにTCPは通っていた話、iptables -A FORWARD が効かない理由、そして iptables -t nat -F でVMがインターネットから消えた話です。

[libvirtのNAT配下VMにRDPしたくて iptables -t nat -F を打ったら、VMがインターネットから消えた]


参考

  • Skyvia Agent の対応OS(2026年8月時点でWindowsのみ提供、Linux/macOSは Coming soon):https://skyvia.com/agent

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?