Docker ComposeとCloudflare Tunnelで安全なファイル同期を実現する
対象読者
クラウドを使わずに自宅サーバーで大容量のデータを安全に同期したい方や、DockerとCloudflare Tunnelを使った環境構築でつまずいている方向けの記事です。
他に、デスクトップパソコンとノートパソコンのデータを同期したい人
複数端末でのデータ同期をしたい人向けです
イメージ:PC1<=>Server<=>PC2(Serverにつなげれば複数端末で可能)
はじめに
どうも、CyberToroCTOの丸山です。
今回は、自宅のLinuxサーバー上に「Syncthing」環境をDocker Composeで構築し、Cloudflare Tunnel経由で管理用のWeb GUIを外部から安全に開けるようにした手順をご紹介します。
複数台のPC(デスクトップとノートPCなど)で開発データや個人的なファイルを同期したい場面は多いと思います。Google DriveやDropboxなどのクラウドストレージを使えば簡単ですが、以下のような要件がある場合はSyncthingが非常に強力な選択肢になります。
- データを外部のクラウドに置きたくない(プライバシー重視)
- 容量を気にせずテラバイト単位で同期したい(自宅サーバーのHDD依存)
- 特定のフォルダだけをP2Pで双方向同期したい
本記事では、ただ構築するだけでなく、実際に運用して「ハマったポイント(権限周りやネットワーク経路)」についてもしっかり解説していきます。
使用技術と動作環境
今回の構築で使用した主な環境やツールは以下の通りです。GCPやOpenAIなどのAPIは使いませんが、Cloudflareの無料枠を活用します。
- OS: Linux(Ubuntu 22.04 LTS 等を想定)
- Docker: Docker / Docker Compose v2
-
イメージ:
lscr.io/linuxserver/syncthing(LinuxServer.ioの公式イメージ) -
Cloudflare: Cloudflare Tunnel(
cloudflared) -
クライアント側: Windows 11 PC (Windows版 Syncthing
syncthing.exeを想定)
アーキテクチャ図
まずは全体像を把握するための構成図です。
最大のポイントは「GUIの公開経路」と「ファイル同期の経路」を完全に分離していることです。
Cloudflare TunnelはHTTP(S)の通信をトンネリングする用途には最適ですが、Syncthingの同期プロトコル(TCP/UDP 22000番)のような独自トラフィックは(有償のWARP等を使わない限り)通常のTunnelでは通せません。
そのため、外部からのファイル同期自体はSyncthing標準の「Global Discovery + Relay」機能に任せ、設定をいじるためのWeb GUIだけをCloudflare経由で安全に公開するという設計にしています。
1. ホスト側の事前準備(権限が最重要)
Dockerでファイルサーバー系のコンテナを立ち上げる際、一番つまづくのがパーミッション(権限)エラーです。
今回使用する linuxserver のイメージは、環境変数 PUID と PGID を指定することで、コンテナ内のプロセスを指定したホスト側のUID/GIDで動作させることができます。
まずは自分のユーザーのUIDとGIDを確認します。
# 現在のユーザーのUIDとGIDを確認
id
# 実行結果例: uid=1001(maruyama) gid=1001(maruyama) groups=1001(maruyama),27(sudo),998(docker)
このUIDとGIDに合わせて、ホスト側にデータを永続化するためのディレクトリを作成し、所有権を変更します。ここがズレていると「Permission denied」で同期フォルダが作成できなくなります。
# ディレクトリの作成(設定用とデータ用の2つ)
sudo mkdir -p /srv/syncthing/{config,data}
# 所有権の変更(UIDとGIDが1001の場合)
sudo chown -R 1001:1001 /srv/syncthing
# Dockerが起動していない場合は起動・自動起動化しておく
sudo systemctl enable --now docker
2. docker-compose.yml の作成と解説
ディレクトリの準備ができたら、Docker Composeファイルを作成します。
初期段階では network_mode: bridge でポート(ports: - "8384:8384" など)を公開する設定でも動くには動きます。しかし、同一LAN内のPCからSyncthingを検知しようとした際、Docker内部のIP(172.17.x.x)が広告されてしまい、接続がタイムアウトする現象が多発しました。
Syncthingのローカル発見(Local Discovery)のブロードキャストを正常に機能させるため、最終的には network_mode: host を採用しました。
任意の作業ディレクトリ(例: ~/docker/syncthing)に移動し、以下のファイルを作成します。
# docker-compose.yml
services:
syncthing:
image: lscr.io/linuxserver/syncthing:latest
container_name: syncthing
hostname: syncthing
network_mode: host # LAN内でのDiscoveryを正常に動かすため
environment:
- PUID=1001 # 先ほど調べたUID
- PGID=1001 # 先ほど調べたGID
- TZ=Asia/Tokyo
volumes:
- /srv/syncthing/config:/config # 設定・鍵・デバイス情報
- /srv/syncthing/data:/data # 同期データの保存先ルート
restart: unless-stopped
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared-syncthing
network_mode: host
restart: unless-stopped
# トンネルを起動するコマンド
command: tunnel --no-autoupdate --config /etc/cloudflared/config.yml run
volumes:
- ./cloudflared-config.yml:/etc/cloudflared/config.yml:ro
- ./tunnel-credentials.json:/etc/cloudflared/credentials.json:ro
depends_on:
- syncthing
必須ポートとファイアウォール(ufw)の設定
network_mode: host を使用しているため、サーバーのOS側のファイアウォール設定が直接影響します。
Syncthingが使用する主なポートは以下の通りです。
| ポート番号 | プロトコル | 用途 |
|---|---|---|
| 8384 | TCP | Web GUIアクセス用(今回はCloudflare Tunnel経由でアクセス) |
| 22000 | TCP / UDP | 実際のファイル同期用トラフィック |
| 21027 | UDP | 同一LAN上のローカルデバイス発見用(IPv4) |
Ubuntu標準のファイアウォール(ufw)を有効にしている場合は、自宅LANのCIDR(例: 192.168.2.0/24)に限定してアクセスを許可しておきます。
# 環境に合わせてLANのセグメントを指定
LAN=192.168.2.0/24
# ルールの追加
sudo ufw allow from "$LAN" to any port 8384 proto tcp comment 'Syncthing Web GUI'
sudo ufw allow from "$LAN" to any port 22000 proto tcp comment 'Syncthing sync TCP'
sudo ufw allow from "$LAN" to any port 22000 proto udp comment 'Syncthing sync UDP'
sudo ufw allow from "$LAN" to any port 21027 proto udp comment 'Syncthing discovery'
# 設定の確認
sudo ufw status numbered
3. Cloudflare Tunnel で GUI を外部公開
Web GUIをインターネットに直接晒すのはセキュリティ上危険です。そこでCloudflare Tunnelを利用し、ポート開放なしで安全にアクセスできるようにします。
まだ cloudflared をホストOSにインストールしていない場合は、公式サイトの手順に従ってバイナリを落とし、ログインを済ませておいてください。
# Cloudflareへのログイン(ブラウザが開くので承認する)
cloudflared tunnel login
# トンネルの新規作成
cloudflared tunnel create syncthing
# DNSレコードのルーティング(例: syncthing.example.com に割り当てる)
cloudflared tunnel route dns syncthing syncthing.example.com
トンネルを作成すると、~/.cloudflared/ ディレクトリに <UUID>.json というクレデンシャルファイルが生成されます。これを docker-compose.yml と同じディレクトリに tunnel-credentials.json としてコピーします。
次に、同じディレクトリに cloudflared-config.yml を作成し、以下のように記述します。
# cloudflared-config.yml
tunnel: <取得したTUNNEL-UUID>
credentials-file: /etc/cloudflared/credentials.json
ingress:
# ドメインへのアクセスをコンテナの8384ポート(GUI)へ流す
- hostname: syncthing.example.com
service: http://127.0.0.1:8384
originRequest:
noTLSVerify: true
# 一致しないアクセスは404で弾く
- service: http_status:404
ここまで完了したら、コンテナを起動します。
docker compose up -d
ブラウザで https://syncthing.example.com にアクセスし、Syncthingの青いダッシュボード画面が表示されれば成功です。
⚠️ セキュリティに関する注意
公開できたからといって油断してはいけません。以下のセキュリティ対策は必須と考えてください。
- GUIのパスワード設定: SyncthingのGUI右上の設定 > GUI から、ユーザー名と強力なパスワードを設定してください。
- Cloudflare Accessの設定: Cloudflare Zero Trustのダッシュボードから、このサブドメインに対してメール認証やGitHub認証などのポリシーを追加することをおすすめします。
4. Windows PC との同期手順とハマりポイント
サーバー側の準備ができたので、クライアント(Windows)側を設定します。
公式のWindows Installer版を使うと環境によっては「WSH script registration is not valid」等のエラーでインストールに失敗することがあります。その場合は、GitHubのReleasesからZIP版(syncthing-windows-amd64-vX.Y.Z.zip)をダウンロードし、任意のフォルダに展開して syncthing.exe を直接実行するのが一番確実です。
同期の基本的な流れ
- 双方のGUIで「IDを表示」から「デバイスID(長い文字列)」をコピーし、お互いに「リモートデバイスの追加」を行う。
- デバイスが相互承認されたら、どちらかのPCで「フォルダを追加」し、共有タブで相手のデバイスにチェックを入れる。
- 相手側のGUIに「フォルダの共有リクエスト」が届くので、承認してローカルの保存先パスを指定する。
実運用でハマったポイントまとめ
① Folder Path(保存先)の指定誤り
サーバー側のGUIで同期フォルダを追加する際、パスに /work や相対パス(desktop など)を指定すると、コンテナのルート領域に作ろうとしてしまい「Permission denied / folder path missing」エラーになります。
正しくは、マウントした /data 配下のパス(例: /data/Desktop)を指定してください。
# もしサーバー側で事前にディレクトリを作っておくならこうする
mkdir -p /srv/syncthing/data/Desktop
chown -R 1001:1001 /srv/syncthing/data/Desktop
# Syncthing GUI側のFolder Pathには以下を入力する
/data/Desktop
② ずっと「切断中(未使用)」から進まない
デバイスの追加が完了しても、「切断中」という表示が出る場合があります。これはデバイスが物理的に繋がっていないケースのほか、**「繋がってはいるが、お互いに1つもフォルダを共有していない(未使用)」**という場合にも表示されます。まずは落ち着いてフォルダのShare設定を行ってみてください。
また、LAN内でどうしても互いを見つけられない場合は、リモートデバイスの設定から「アドレス(Addresses)」を dynamic から tcp://192.168.2.148:22000, dynamic のようにサーバーのLAN IPを直接指定すると一発で繋がります。
③ "folder marker missing" の警告で同期が止まる
同期フォルダの直下には、Syncthingが管理用に作る .stfolder という空の隠しディレクトリ(マーカー)が必須です。
「パスを変更した直後」や「誤ってディレクトリの中身を全削除した」場合、このマーカーが消えるとSyncthingは「誤ってデータが消去されたと判断して、他のPCのデータまで巻き添えで消さないように」安全装置として同期を意図的に停止させます。
パスが間違っていないと確信できる場合のみ、対象のディレクトリで mkdir .stfolder を実行してマーカーを手動作成し、GUIで再スキャンをかけてください。
④ 無視パターン(.stignore)の挙動
「このフォルダの中の node_modules や .git は同期したくない」という場合はGUIの「無視するパターン」にルールを書きます。
ルート直下の特定のフォルダだけを除外したい場合は、以下のように記述するのが確実です。
/Virtual Machines/
/My Games/
/.git/
注意点として、無視設定は「これから同期するもの」にしか効きません。 すでに同期されてしまったファイルを無視パターンに追加しても自動では消えないため、不要であれば手動で削除する必要があります。
最後に
自宅サーバーにSyncthingを載せること自体はDocker Composeを使えば一瞬ですが、実運用に乗せるまでには「権限(PUID/PGID)」「Folder Pathの指定先」「Dockerネットワークのホストモード」「GUIと同期の経路分離」など、いくつかのつまずきどころがありました。
特にCloudflare TunnelはGUI公開に非常に相性が良く、自宅ルーターのポート開放(穴あけ)なしで出先から安全に管理画面へ入れるのは大きなメリットです。一方で、同期プロトコルそのものはSyncthingネイティブの接続(RelayやLAN直結)に任せるという切り分け設計が重要だと感じました。
自宅サーバーでのファイル同期環境構築の参考になれば幸いです。
参考文献
- linuxserver/syncthing公式ドキュメント: https://docs.linuxserver.io/images/docker-syncthing/
- Syncthing 公式ドキュメント: https://docs.syncthing.net/
- Cloudflare Tunnel: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/