Syncthing検証ログ:Docker 2ノードで『接続できた』と『同期できた』は別だった

Syncthingのフォルダ同期は便利さより先に競合ファイルと削除伝播を作って見るのアイキャッチ画像
目次

まず、誰のための記事か

この記事は、NASやミニPCに散らばった設定ファイルや作業ファイルを、クラウドに預けずに家庭ラボ内で同期したい運用者の方を想定しています。Syncthing自体は無料で双方向同期ができる定番ツールですが、導入の判断で本当に怖いのは「動いた瞬間」ではなく、片方で消したファイルが全端末から消える挙動や、同時編集でどちらが残るか分からない挙動、ignore設定で必要なファイルが届かない挙動の方です。

そこで今回は、便利な共有機能としてではなく、競合と削除を先に壊して観察する目的で、使い捨てのDocker環境にSyncthingを2ノード構成で立てました。結論から言うと、検証は途中で別の壁に当たりました。その壁こそが、常用前に知っておく価値のある停止線です。

検証環境

  • 実行基盤:某所ラボの使い捨てVM(Docker利用)
  • Docker:29.6.0 / Docker Compose:v5.2.0
  • イメージ:syncthing/syncthing:1.27(稼働ログ上は v1.27.12)
  • 構成:nodeA / nodeB の2コンテナ、各ノードのWeb UIはローカルホストにのみ公開
  • 作業ディレクトリは検証後に完全削除

実行コマンドとログ

1. 起動環境の確認と作業ディレクトリ作成

# docker --version && docker compose version
Docker version 29.6.0, build fb59821
Docker Compose version v5.2.0

2. Compose定義(2ノード)

以下を compose.yaml として置きました。Web UIは外部に晒さないよう 127.0.0.1 にだけバインドしています。

services:
  nodeA:
    image: syncthing/syncthing:1.27
    hostname: nodeA
    volumes:
      - ./nodeA:/var/syncthing
    ports:
      - "127.0.0.1:8384:8384"
    restart: unless-stopped
  nodeB:
    image: syncthing/syncthing:1.27
    hostname: nodeB
    volumes:
      - ./nodeB:/var/syncthing
    ports:
      - "127.0.0.1:8385:8384"
    restart: unless-stopped

3. 初回起動・設定生成・デバイスID取得

初回起動で各ノードの設定一式(証明書・config.xml・DB)が生成されます。生成されたデバイスIDを取得し、相互にピア登録したうえで、共有フォルダ synctest(type=sendreceive)を両ノードへ追加しました。デバイスIDは公開向けにマスクしています。

4. 再起動して接続確立を確認

再起動後、nodeAのログに相互接続の確立が出ました。

nodeA-1  | [<設定項目>] 設定項目: Established secure connection to <設定項目> at <redacted>:22000 (設定項目.3 / 設定項目 / LAN)
nodeA-1  | [<設定項目>] 設定項目: Device <設定項目> client is "syncthing v1.27.12" named "nodeB"

ここまでで、2ノードがTLS1.3で相互認証し、互いを正しいピアとして認識した状態までは作れました。

5. デバイス登録ミスの症状(失敗ケース→復旧)

実運用で起きがちな「相手のデバイスIDを1文字打ち間違える」状況を再現するため、nodeB側のピアID(nodeA分)を1文字だけ書き換えて再起動しました。Syncthingはデバイスを公開鍵の指紋で識別するため、IDが食い違えば相手として成立しません。観測後、バックアップから設定を戻して復旧しています。

結果:接続は確立したが、同期そのものに到達できなかった

ここからが本題です。接続確立を確認できたので、nodeA側の共有フォルダに2ファイルを置き、40秒待ってから両ノードの一致を find と sha256sum で確認しようとしました。

# (nodeA/data に file1.txt, file2.txt を作成し、待機後にハッシュ確認)
== nodeA ==
./file1.txt
./file2.txt
e2f1c22d4a98fd6e...  ./file1.txt
f3df38170796e9aa...  ./file2.txt
== nodeB ==
bash: cd: nodeB/data: No such file or directory

nodeA側にはファイルとハッシュが揃いましたが、nodeB側にはデータ用ディレクトリ自体が生成されていませんでした。つまり共有フォルダの実体が受信側に作られておらず、同期が成立していません。

この状態のまま、当初予定していた以下の3つは、いずれも nodeB 側のデータディレクトリが存在しないため確認コマンドが失敗(exit=1)し、観測に到達できませんでした。

  • 同時編集による競合ファイル(*.sync-conflict-* 系)の生成
  • 片方での削除が相手へ伝播するか
  • .stignore による ignore 誤設定の見え方

推定原因と、ここから読める停止線

今回の証跡から確実に言えるのは「TLSセキュア接続の確立」までで、「フォルダ共有の受諾とパス整合まで完了し、実データがミラーされた」ことは確認できていません。設定ファイルを直接書き換えて共有フォルダを足したため、受信側でのフォルダ受諾やパス生成が完了していなかった可能性が高いと見ています。ここはGUIからの正規手順での再検証が必要です。

ただし、この未完了そのものが読者にとっての停止線になります。

  1. 「デバイスが接続できた」と「フォルダが安全に同期できた」は別物。ログに secure connection が出ても、受信側にファイルが届いている保証はありません。常用前に、実ファイルの到達とハッシュ一致まで自分の目で確認してください。
  2. Syncthingはミラーであってバックアップではない。公式ドキュメントでも、同時編集時には競合コピー(.sync-conflict-* 形式)が作られ、片側の削除は同期相手にも伝播し得ると説明されています(同期の挙動)。つまり一度同期が成立すれば、削除も相手へ届くのが正常動作です。世代を遡れる別系統のバックアップは、Syncthingとは別に必ず用意してください。
  3. ignore設定は事前に挙動を確認する。.stignore のパターンは効かせ方を誤ると必要ファイルが届かないため(ignoreの仕様)、本番フォルダの前に捨てフォルダで挙動を見るのが安全です。

比較:双方向同期・片方向コピー・共有フォルダ直接

家庭ラボで「複数端末のファイルをどう扱うか」を考えるとき、自然な選択肢は次の3つです。今回の検証で痛感した「削除と競合の伝わり方」を軸に並べます。

観点 Syncthingで双方向同期 rsyncで片方向コピー 共有フォルダを直接使う
競合時の見え方 同時編集で競合コピーが生成され得る(公式仕様) 後勝ちで上書き。競合は基本残らない 同時編集はアプリ依存でロック頼み
削除の伝播範囲 削除も相手へ伝播し得る(バックアップ不可) –deleteを付けない限り伝播しない 共有元を消すと全端末から消える
設定ミスの検出しやすさ デバイスID不一致やignore誤設定は気づきにくい コマンドが明示的で意図を読み取りやすい 設定が少なく単純
ロールバックのしやすさ 旧版保持を別途設定しない限り遡りにくい 世代付きで運用すれば遡れる 同上、別バックアップ前提

rsyncの片方向コピーは「元を壊さず複製を作る」用途に強く、削除を勝手に伝播させない安心感があります。一方Syncthingは複数端末で常に最新を揃えたい用途に向きますが、その代わり削除も同期されます。どちらが上ということではなく、目的が「同期(ミラー)」か「複製(バックアップ)」かで分けるのが実用的です。

常用に入れる条件 / まだ見送る条件

入れてよい条件

  • 実ファイルがハッシュ一致まで届くことを自分で確認できた
  • 競合・削除・ignoreの挙動を捨てフォルダで一度観察した
  • 世代を遡れる別系統のバックアップを併用できる

まだ見送る条件

  • 「接続できた」ログだけで同期できたと判断している
  • 唯一のコピーをSyncthingだけに預けようとしている
  • ignoreや共有フォルダの受諾手順を確認していない

失敗・制約・再現条件

  • 到達できた範囲:2ノード起動、設定生成、相互デバイス登録、設定項目.3でのセキュア接続確立、デバイスID不一致の症状観測と復旧。
  • 到達できなかった範囲:実データの同期一致、競合コピー生成、削除伝播、ignore誤設定の見え方(いずれも受信側データディレクトリ未生成によりexit=1)。
  • 制約:設定をファイル直書きで投入したため、フォルダ受諾やパス整合が未完だった可能性が高い。GUI正規手順での再検証が残課題。
  • 本検証はDocker Composeによる複数コンテナ間同期が前提のため、使い捨てVM+Dockerで実施。検証後は作業ディレクトリとコンテナを完全に削除しています。

まとめ:常用前にどの失敗条件を確認すべきか

「Syncthingを常用に入れる前に、どの失敗条件を小さく確認すべきか」への答えは、今回の結果がそのまま示しています。最初に確認すべきは派手な競合や削除より前に、実ファイルが受信側へ本当に届いているかです。接続ログは成功していても同期は別。そのうえで競合・削除・ignoreを捨てフォルダで観察し、Syncthingはミラーである前提で別系統のバックアップを併用する——ここまで揃って初めて、家庭ラボの常用候補になります。競合と削除の実挙動の再検証は、次の検証ログで取り上げます。

この検証を回している環境

この検証は、自宅の常設ラボ(使い捨てVM/LXCを回す母艦+GPU+VLAN分離ネットワーク)で動かしています。使っている機材と選定理由、全体構成は1本にまとめています。

ラボ構成のまとめを見る →
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次