はじめに
以前の記事で、PT2を使ったUbuntu録画サーバーを再構築しました。現在はMirakurun+EPGStationを使って録画しています。
ただ、この録画サーバーが置いてあるのは自宅とは別のテレワーク部屋です。自宅とテレワーク部屋のネットワークは拠点間VPNで接続しているため、自宅から録画サーバーへアクセスすること自体はできます。しかし、自宅からVPN越しに録画した番組を再生しようとすると、通信状況などによってはスムーズに再生できないことがあります。せっかく録画した番組なのに、再生中に止まったり待たされたりするのは少々不便です。
そこで「それなら、録画が終わったファイルを自宅側へ持ってきてしまえばいいのでは?」と考えました。
自宅ではRaspberry Piを常時稼働させているため、そこへUSB HDDを接続し、テレワーク部屋の録画サーバーから録画ファイルを自動転送することにしました。転送に使用しているサービスは、lsyncd+rsync+SSHです。録画サーバー側でファイルが作成・更新されるとlsyncdが検知し、rsyncを使ってSSH経由で自宅のRaspberry Piへ転送します。
通信経路は、もともと自宅とテレワーク部屋の間に構築している拠点間VPNを利用します。これなら録画中はテレワーク部屋の録画サーバーを使用しつつ、録画が終わったデータは自宅側にも自動的に転送できます。
自宅で録画済み番組を見るときには、VPN越しに大容量の動画をストリーミングするのではなく、自宅にあるデータを再生できるというわけです。
今回は、以前から運用しているこの仕組みを改めて確認しながら、UbuntuからRaspberry PiへVPN越しに録画ファイルを自動転送する方法を整理してみます。
【関連記事】
2026年でもPT2は使えるのか? Ubuntu 24.04で録画サーバーを再構築してみた【Mirakurun・EPGStation】
自宅とテレ部屋のYAMAHA RTX1200をRTX1210へ交換してみた【既存CONFIG・ネットボランチDNS・拠点間VPN移行】
今回の構成
現在は以下のような構成になっています。
今回もマーメイドで記載してみました。

VPNそのものについては以前から構築しているものを利用するため、この記事ではVPNの構築方法については扱いません。
録画サーバーから、192.168.100.66のRaspberry PiへIP通信できる状態からスタートします。
lsyncdやrsync側に「VPNを使用する」という特別な設定があるわけではありません。録画サーバーからRaspberry Pi宛ての通信がルーティングによってVPNへ流れるため、通常のSSH通信を行えば結果としてVPN越しの転送になります。
以前は中継サーバーを使っていた
実は以前から似たような仕組みで録画データを転送していました。当時の録画サーバー ma-tv はOSがかなり古く、Raspberry Piへ直接rsyncすることができませんでした。
そのため、以下のように、ma-ffmpeg を間に挟んで転送していました。

その後、録画サーバーをUbuntu 24.04の ma-tv2 として再構築したため、現在は以下のように直接転送できるようになっています。今回の記事では、現在の構成を整理していきます。
使用環境
送信側:Ubuntu録画サーバー
今回確認した環境は以下のとおりです。
ホスト名:ma-tv2
OS :Ubuntu 24.04.1 LTS
lsyncd :2.2.3
rsync :3.2.7
OpenSSH :9.6p1録画データは、/ma-tv2/以下に保存されています。
受信側:Raspberry Pi
受信側は常時稼働させているRaspberry Piです。
OS :Raspbian GNU/Linux 11 (bullseye)
rsync :3.2.3
OpenSSH :8.4p1
IP :192.168.100.66USB HDDとしてWD Elements 8TBを接続しています。
ファイルシステムはext4で、/ma-tvへマウントしています。
8TBのHDDですが、Linux上では約7.3TiBとして認識されています。
Raspberry Pi側のUSB HDDを準備する
まずはRaspberry Pi側で、録画ファイルを受け取るHDDを使えるようにします。私の環境ではWD Elementsの8TB HDDをUSB接続しています。
HDDを確認する
接続されているストレージを確認します。
lsblk -o NAME,SIZE,FSTYPE私の環境ではHDDが、/dev/sdaとして認識されています。
USB HDDの情報については、次のコマンドでも確認できます。
udevadm info --query=property --name=/dev/sda | \
grep -E 'ID_BUS|ID_MODEL|ID_VENDOR'実際の環境では、
ID_VENDOR=WD
ID_MODEL=Elements_25A3
ID_BUS=usbとなっていました。
マウントポイントを作成する
HDDのマウント先として、/ma-tvを使用します。
存在しない場合は作成します。
sudo mkdir -p /ma-tv今回のHDDはすでにext4で使用しているものなので、以下のコマンドでマウントできます。
sudo mount -t ext4 /dev/sda /ma-tv注意
既存データの入っているHDDに対して mkfs は実行しないでください。mkfs はファイルシステムを作成するためのコマンドです。今回のように既存のHDDを利用する場合には必要ありません。
USB HDDを自動マウントする
今回、この記事を書くために実際の環境を確認していて、一つ気付いたことがありました。
このUSB HDD、これまで手動でマウントしていました。
過去のコマンド履歴にも、マウントコマンドが残っていました。
mount -t ext4 /dev/sda /ma-tv/せっかくなので今回、Raspberry Piを再起動しても自動的にマウントされるよう /etc/fstab へ登録することにしました。
UUIDを確認する
まずHDDのUUIDを確認します。
sudo blkid /dev/sda例えば、
/dev/sda: UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" BLOCK_SIZE="4096" TYPE="ext4"のように表示されます。
記事では実際のUUIDではなく、上記のようなダミー値を使用します。
fstabを編集する
念のため、編集前にバックアップを取得します。
sudo cp -p /etc/fstab /etc/fstab.bak続いて、
sudo vi /etc/fstabで編集し、以下を追加しました。
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /ma-tv ext4 defaults,nofail 0 2/dev/sda ではなくUUIDを指定しています。USB HDDは接続状況などによって /dev/sda や /dev/sdb といったデバイス名が変わる可能性があります。そのため、特定のHDDを指定する用途ではUUIDを使う方が確実です。
fstabの設定を確認する
いきなり再起動するのではなく、まず設定に問題がないか確認しました。
sudo findmnt --verify結果は、
Success, no errors or warnings detectedとなりました。
さらに一度アンマウントして、
sudo umount /ma-tv
sudo mount -aとします。
その後、
mount | grep ' /ma-tv '
df -h /ma-tvで確認しました。
/dev/sda on /ma-tv type ext4 (rw,relatime)mount -a 実行後、/dev/sda が /ma-tv に正常にマウントされていることを確認できました。

これでRaspberry Pi側の保存先が準備できました。
SSHの公開鍵認証を準備する
次にUbuntu録画サーバーからRaspberry Piへ、SSHで接続できるようにします。
lsyncdによる転送は自動で行われるため、毎回SSHのパスワードを入力することはできません。
そこでSSHの公開鍵認証を使用します。
「証明書」と呼びたくなるところですが、今回使用しているのはSSHの秘密鍵と公開鍵による認証です。
rsync専用のSSH鍵
現在の環境では、Ubuntu録画サーバーに以下のrsync用の鍵があります。
/root/.ssh/id_rsa.rsync
/root/.ssh/id_rsa.rsync.pub
新しく作成する場合の例は、以下のコマンドですsudo ssh-keygen -t rsa -b 3072 -f /root/.ssh/id_rsa.rsync以下の二つが生成されます。
id_rsa.rsync ← 秘密鍵
id_rsa.rsync.pub ← 公開鍵秘密鍵は送信側から外へ出しません。公開鍵だけをRaspberry Pi側へ登録します。
Raspberry Piへ公開鍵を登録する
Raspberry Pi側では、/root/.ssh/authorized_keysに公開鍵を登録しています。
現在の環境を確認したところ、authorized_keys には2本の公開鍵が登録されていました。
そのうち1本が現在の ma-tv2 用です。
送信側で、
sudo ssh-keygen -lf /root/.ssh/id_rsa.rsync.pubRaspberry Pi側で、
sudo ssh-keygen -lf /root/.ssh/authorized_keysRaspberry Pi側では、authorized_keysに登録されている公開鍵のfingerprintを確認します。
送信側の id_rsa.rsync.pub のfingerprintと比較し、一致する鍵が登録されていることを確認しました。
これで、
ma-tv2
秘密鍵
/root/.ssh/id_rsa.rsync
│
│ SSH公開鍵認証
▼
Raspberry Pi
公開鍵
/root/.ssh/authorized_keysという関係になっています。
rootユーザーについて
私の既存環境では、Raspberry PiへのSSHログインにrootユーザーを使用しています。
Raspberry Pi側でも、PermitRootLogin yesとなっています。
ただし、この記事のために PermitRootLogin yes を設定したわけではありません。
もともとSSHログインを許可していた既存環境を利用しています。
また、今回のrsyncによる自動転送ではパスワードを入力しているわけではなく、先ほどのrsync用SSH鍵を使用して公開鍵認証しています。
新規に環境を作る場合には、rootを使用せず転送専用ユーザーを作成する方法も検討した方がよいでしょう。
この記事では、実際に運用している環境に合わせてrootを使用して進めます。
まずrsync単体で転送を確認する
いきなりlsyncdを設定するのではなく、先に、SSH+rsyncだけでRaspberry Piへ転送できることを確認しておくと、問題が起きたときの切り分けが楽になります。
例えばテストファイルを用意し、
sudo rsync -avh \
-e "ssh -i /root/.ssh/id_rsa.rsync -o UserKnownHostsFile=/root/.ssh/known_hosts" \
/ma-tv2/test.txt \
root@192.168.100.66:/ma-tv/として転送します。
Raspberry Pi側で、
ls -l /ma-tv/test.txtとしてファイルが存在すれば、
Ubuntu
│
rsync
│
SSH
│
VPN
│
▼
Raspberry Piという経路で転送できています。
ここまで成功してからlsyncdを設定します。
lsyncdをインストールする
Ubuntu録画サーバー側へlsyncdをインストールします。
sudo apt update
sudo apt install lsyncd rsync今回実際に使用しているバージョンは、
lsyncd 2.2.3
rsync 3.2.7でした。
lsyncdを設定する
現在の環境では、
/etc/lsyncd/lsyncd.conf.luaを設定ファイルとして使用しています。
実際に使用している設定がこちらです。
settings {
logfile="/var/log/lsyncd/lsyncd.log",
statusFile="/var/log/lsyncd/lsyncd.status",
nodaemon=false,
}
sync {
default.rsyncssh,
source="/ma-tv2/",
host="root@192.168.100.66",
targetdir="/ma-tv/",
exclude={
"*.ts",
"*.mp4_tmp.ts",
"*.m2ts",
"video_tmp_ts/",
"ts/",
"lost+found/",
"thumbnail/"
},
delay=10,
delete=true,
rsync = {
archive = true,
compress = true,
rsh = "/usr/bin/ssh -i /root/.ssh/id_rsa.rsync -o UserKnownHostsFile=/root/.ssh/known_hosts"
}
}それぞれ見ていきます。
転送元と転送先
source="/ma-tv2/",
host="root@192.168.100.66",
targetdir="/ma-tv/",source が監視するローカルディレクトリです。
今回は録画データが入っている、/ma-tv2/を監視しています。
転送先はVPN先の、root@192.168.100.66:/ma-tv/です。
ここでVPN用の特殊な設定をしていないところもポイントです。192.168.100.66 への経路がVPNを向いているため、lsyncdから見れば通常のSSH接続と変わりません。
転送したくないファイルを除外する
除外設定は、以下のように設定しています。
exclude={
"*.ts",
"*.mp4_tmp.ts",
"*.m2ts",
"video_tmp_ts/",
"ts/",
"lost+found/",
"thumbnail/"
},ここは少し重要です。
この設定は、MP4だけを転送するという意味ではありません。
転送したくないファイルを除外して、それ以外を転送する設定です。
例えば、
*.ts
*.m2tsなどは転送対象外になりますが、完成した .mp4 は除外されていないので転送されます。
一方で .txt も除外されていないため、.txt ファイルを置けばそれも転送されます。
後ほど、この性質を利用してテストしてみます。
なお、*.mp4_tmp.tsは *.ts にも該当するため、現在の設定では実質的には重複しています。
昔作った設定をそのまま使っているため残っていますが、整理するなら削除してもよさそうです。
変更検知から同期までの待ち時間
lsyncdがファイルシステムの変更を検知すると、それらをまとめてrsyncへ渡します。
今回は delay=10 としています。変更を検知すると、lsyncdは一定時間変更をまとめてからrsyncを実行します。そのため、変更した瞬間に転送されるのではなく、短時間遅れて追従する動きになります。
delete=trueには注意
今回の設定では、delete=true,としています。
これによって、送信元で削除されたファイルはRaspberry Pi側からも削除されます。
つまり、以下のようになります。
Ubuntu Raspberry Pi
file.mp4 ─────→ file.mp4
↓ 削除
なし ─────→ なしそのため、この構成を「バックアップ」と呼ぶのは少し違います。
誤って録画サーバー側から削除すれば、Raspberry Pi側にも削除が反映されます。
今回の目的は、録画サーバーのファイルを別拠点のRaspberry Piへ同期・転送することです。
削除したファイルも保存しておきたい場合は、delete=true の扱いを変更する必要があります。
なお、今回の同期はUbuntu録画サーバーからRaspberry Piへの一方向です。Raspberry Pi側でファイルを作成・変更・削除しても、その操作が録画サーバー側へ反映されることはありません。
compress=trueについて
今回の設定では、rsyncのオプションとして compress=true も指定しています。
これは転送時にデータを圧縮する設定です。
ただし、今回主に転送しているMP4ファイルは、すでに圧縮された動画データです。そのため、compress=true を指定しても転送量を大きく減らせるとは限らず、圧縮・展開のためのCPU負荷が増える場合があります。
今回は以前から運用している設定をそのまま使用していますが、録画ファイルの転送が中心であれば、compress=false とすることも検討できそうです。
SSH鍵を指定する
rsync部分では、以下のように設定をしています。
rsh = "/usr/bin/ssh -i /root/.ssh/id_rsa.rsync -o UserKnownHostsFile=/root/.ssh/known_hosts"具体的にSSH鍵を指定している部分は以下です。
/root/.ssh/id_rsa.rsyncこれによりlsyncdからrsyncが実行された際も、先ほど準備したSSH鍵を使ってRaspberry Piへ接続できます。
lsyncdを起動する
設定ができたらlsyncdを起動します。
sudo systemctl start lsyncd状態を確認します。
sudo systemctl status lsyncd現在の環境では、
Active: active (running)となっています。
今回確認した時点では、9月11日の起動から1週間以上継続して動いていました。

OS起動時にもlsyncdが起動するよう、自動起動を有効にしておきます。今回の環境でも確認したところ enabled となっていました。
sudo systemctl enable lsyncd
lsyncdの状態を確認する
設定している、
statusFile="/var/log/lsyncd/lsyncd.status",によって状態ファイルも作られています。
sudo cat /var/log/lsyncd/lsyncd.status確認時には、
There are 0 delays
Filtering: nothing.
Inotify watching 143 directories.となっていました。
実際に /ma-tv2/recorded/ 以下の録画ディレクトリが多数監視されています。

実際に自動転送を試す
設定だけでは本当に動いているのか分からないので、実際にファイルを作って試してみました。
Ubuntu録画サーバー側で、
date | sudo tee /ma-tv2/lsyncd_test.txtを実行します。
date の出力を tee へ渡して、現在日時が書かれたテストファイルを作成しています。
実際には、
Mon Sep 21 05:50:16 PM JST 2026という内容のファイルが作成されました。

VPN先のRaspberry Piに現れた
少し待ってRaspberry Pi側を確認します。
ls -la /ma-tv/すると、
-rw-r--r-- 1 root root 32 Sep 21 17:50 lsyncd_test.txtが作成されていました。VPNを越えて自動的に転送されています。

lsyncdのログを確認する
Ubuntu側で、
sudo tail -100 /var/log/lsyncd/lsyncd.logを確認します。
今回のテストは、
Mon Sep 21 17:50:28 2026 Normal: Calling rsync with filter-list of new/modified files/dirs
/lsyncd_test.txt
/
Mon Sep 21 17:50:30 2026 Normal: Finished (list): 0と記録されていました。
テストファイルを作成したのが17:50:16。
lsyncdが17:50:28にrsyncを呼び出し、17:50:30には終了しています。
delay=10 の設定ともおおむね一致する動きです。
削除も同期されるか試す
今度は送信側でテストファイルを削除します。
sudo rm /ma-tv2/lsyncd_test.txtRaspberry Pi側を確認すると、
/ma-tv/lsyncd_test.txtも削除されました。
ログを見ると、
Mon Sep 21 17:51:58 2026 Normal: Calling rsync with filter-list of new/modified files/dirs
/lsyncd_test.txt
/
Mon Sep 21 17:52:00 2026 Normal: Finished (list): 0となっています。
これで delete=true が実際に動作していることも確認できました。


実際の録画ファイルでも動いている
もちろんテストファイルだけでなく、実際の録画データでも動いています。
ログには、
Normal: Calling rsync with filter-list of new/modified files/dirs
/recorded/<番組名>/<録画ファイル>.mp4
/recorded/<番組名>/
/recorded/
/
Normal: Finished (list): 0といった形で、MP4ファイルが転送された記録が残っています。

録画・エンコードが終わってMP4が作成されれば、その変更をlsyncdが検知し、VPN先のRaspberry Piへ自動的に転送してくれます。手動でコピーする必要はありません。
rsyncサービスが停止しているけど大丈夫?
環境を確認している途中で、sudo service rsync statusを実行すると、
Active: inactive (dead)となっていました。
一瞬「rsyncが動いていない?」と思いましたが、今回の構成では問題ありません。
使用しているのは、default.rsyncsshです。
lsyncdが必要なタイミングでrsyncコマンドを起動し、それをSSH経由で実行しています。
そのため、rsyncデーモンを常時起動しておく構成ではありません。
以下のような動きになります。
lsyncd
│
├─ 変更を検知
│
└─ rsyncを起動
│
SSH
│
VPN
▼
Raspberry Pi余談:lsyncdが数GBもメモリを使っている?
今回とは別のタイミングで、systemctl status lsyncdを確認したところ、Memoryが約2.5GB、過去のピークが約13GBと表示されたことがありました。
「lsyncdがメモリリークしている?」と思って調べてみました。
ところが、lsyncdプロセスそのものを確認すると、実際のRSSはわずか数MB程度でした。
さらにcgroupを確認すると、
cat /sys/fs/cgroup/system.slice/lsyncd.service/memory.stat大部分を占めていたのは、以下でした。
file
inactive_fileつまり、lsyncd本体が数GBのメモリを抱え込んでいたわけではなく、大容量ファイルを転送した際のファイルページキャッシュがcgroupのメモリ使用量として計上されていました。Linuxでは必要に応じてこのキャッシュを回収できるため、今回の環境では特に対処していません。
これはこれで掘り下げると長くなりそうなので、別記事にしても面白そうです。
まとめ
今回は、Ubuntu録画サーバーからVPN先のRaspberry Piへ録画ファイルを自動転送している環境を改めて整理しました。
現在の構成は、以下です。

lsyncdがファイルの変更を監視し、必要になったときだけrsyncを起動してSSH経由でRaspberry Piへ転送します。VPNについても、lsyncd側で特別な設定をする必要はなく、Raspberry Piへの経路がVPNへ向いていれば通常のSSH通信として利用できます。
また今回の設定では delete=true としているため、送信元からファイルを削除するとRaspberry Pi側からも削除されます。そのため、これは「削除しても残り続けるバックアップ」というより、別拠点への自動同期・ミラーリングとして使用しています。
そして今回、昔作った環境を記事にするため一つずつ確認してみたところ、USB HDDを手動でマウントしていたことや、昔使っていた中継サーバーのSSH公開鍵が残っていることにも気付きました。
昔作った仕組みは動いているとなかなか触らないものですが、改めて構成を追いかけてみると色々出てきます。
ひとまずこれで、以下の流れで自動で録画データを流せる仕組みを整理できました。
録画 → MP4作成 → lsyncdが検知 → rsync+SSH → VPN → Raspberry Pi+8TB HDD
