前の記事では、Windows の PC で iPhone をバックアップし、毎晩 NAS にコピーするようにしました。ただ、PC で取るには PC を起動してアプリを開く必要があります。
そこで今回は、家で動かしっぱなしのラズパイ(Raspberry Pi 3 Model B)に iPhone を USB で接続して、バックアップが取れるかを試しました。結果は、PC と同じ丸ごとのバックアップが 29 分で取れました。この記事では、その手順と、ラズパイならではのつまずきをまとめます。
この記事のポイント
- ラズパイに libimobiledevice(iPhone とやり取りする Linux のツール)をインストール
- iPhone を USB で接続して「信頼」し、SD カードに丸ごとバックアップ(8.5GB・約 35,000 ファイル・29 分)
- そのバックアップを NAS に送る(29 分・ファイルの数が一致するのを確認)
- 挿すだけで、声でパスコードを頼み、バックアップから NAS への送りまで済むようにした(2 回目からは 5 分ほど:バックアップ 2 分半・NAS 2 分)
使ったのは Debian 13(trixie)の Raspberry Pi OS、libimobiledevice 1.3.0 です。
1. ツールをインストールする
sudo apt update
sudo apt install libimobiledevice-utils usbmuxd
usbmuxd は、USB で接続した iPhone とやり取りするための常駐のプログラムです。libimobiledevice-utils に、バックアップの idevicebackup2 や、ペアリングの idevicepair が含まれています。
2. iPhone を接続して信頼する
idevicepair pair
iPhone に「このコンピュータを信頼しますか?」と出るので「信頼」を押し、パスコードを入力します。そのあと、もう一度 idevicepair pair を打つと「SUCCESS」と出ます。
つまずいた所:接続しても iPhone に何も出ない
最初は、接続しても iPhone に「信頼」の画面が出ませんでした。ラズパイの記録(dmesg)を見ると、USB のエラー(error -32)が出ていました。同じケーブルのまま、ラズパイの別の USB の差し口に挿し直すと、すぐに「信頼しますか?」が出ました。差し口との相性か、挿し方が浅かったようです。
3. バックアップを取る
mkdir -p ~/iphone-backup
idevicebackup2 backup --full ~/iphone-backup
始めると、iPhone にパスコードの入力を求められます。入力するまで先に進みません。これは Windows のときと同じです。
わたしは Windows のアプリで「ローカルバックアップを暗号化」をオンにしてあったので、ラズパイで取ったバックアップも暗号化されました(記録に「Backup will be encrypted.」と出ます)。暗号化の設定は iPhone の側に残るので、ラズパイでパスワードを入れ直す必要はありません。
Windows を使っていないときは、1 回目の前にラズパイで暗号化をオンにします。暗号化しないと、保存したパスワードやヘルスケアのデータがバックアップに残りません。
idevicebackup2 encryption on
結果
- かかった時間:29 分(PC のときとほぼ同じ)
- 大きさ:8.5GB・35,340 ファイル
- 最後に「Backup Successful.」と出れば完了
2 回目からは –full を付けずに打つと、変更があった分だけを取ります。
つまずいた所:ラズパイが熱くなり、電気も足りなくなる
バックアップの途中で、ラズパイの監視(負荷が上がると声で知らせる仕組み)が「重くなっています」としゃべりました。調べると、毎日の DB のバックアップと重なって負荷が 5.7(4 コアなので少し詰まっている程度)、温度は 79.5℃ まで上がっていました。
さらに、電圧の記録(vcgencmd get_throttled)に、電圧が足りなくなったことがある記録が残っていました。iPhone をラズパイの USB で充電しながらバックアップしたので、電気が足りなくなったのだと思います。ラズパイは熱や電圧が危ないと自分で速度を落とすので、壊れはしませんが、時間がのびます。

次からの対策
- iPhone は、ある程度充電してから接続する
- できれば電源つきの USB ハブを通す
- ほかの重い処理(DB のバックアップなど)と時間をずらす
4. NAS に送る
SD カードだけだと、SD カードが壊れたときに一緒になくなるので、NAS にも送りました。ファイルが 35,000 個あるので、1 個ずつ送るより、tar で 1 本にまとめて ssh で流すほうが速く済みます。
前もって、ラズパイから NAS に ssh の鍵(パスワードなし)でログインできるようにし、~/.ssh/config に NAS の名前を付けておきます。NAS には tar と rsync が要ります(5 で rsync を使います)。
tar -C ~ -cf - iphone-backup | ssh <NAS の名前> "mkdir -p <NAS の置き場所> && tar -C <NAS の置き場所> -xf -"
29 分で送り終え、SD カードと NAS のファイルの数がどちらも 35,338 件で一致しました(「ファイル」だけを数えた数)。
5. 挿すだけで取れるようにする
毎回コマンドを打つのは手間なので、iPhone をラズパイに挿すだけで、バックアップから NAS への送りまで済むようにしました。
決まった時間に自動で取る形にしなかったのは、バックアップを始めるたびに iPhone にパスコードの入力が出るからです。夜中に始めても、パスコードを入力するまで止まったままになります。そこで「挿したら始めて、声でパスコードを頼む」形にしました。
動きの流れ
- cron で 1 分ごとに、iPhone が接続されているかを見る(idevice_id -l)
- 接続されていたら、声で「パスコードを入れてください」と知らせる
- iPhone でパスコードを入力すると、変更があった分だけバックアップする
- 完了したら、変更があった分だけ NAS に送り(rsync)、声で「完了しました」と知らせる
- 失敗したときは、声と LINE で知らせる(LINE は notify.py を使うとき)。挿したままなら次は始めず、一度抜いて挿し直すと取り直す
1 日 1 回までにしてあるので、充電のためだけに挿しても、2 回目は取りません。信頼していない iPhone を挿したときも何もしません。パスコードを入力せずに 2 時間たつと、止めて失敗にします(それでも止まらないときは、その 1 分後に強く止めます)。
実際の声を聞いてみる
今回のために VOICEVOX で作った 3 つの声です。ラズパイのスピーカーから流れているのと同じファイルです。▶ を押すと再生します(音が出ます)。
挿したとき:「アイフォンのバックアップを始めます。アイフォンに、パスコードを入れてください。」
完了したとき:「アイフォンのバックアップが、完了しました。」
失敗したとき:「アイフォンのバックアップが、失敗しました。調べてください。」
音声:VOICEVOX:里石ユカ(つぼみ)
声は、PC で VOICEVOX を起動しておき、次のスクリプトで作りました(Node.js 18 以上)。できた WAV をラズパイの ~/notify/sounds/(下の、声だけの say なら ~/sounds/)に置きます。話者の番号(126)は VOICEVOX のバージョンで変わることがあるので、http://127.0.0.1:50021/speakers で確認してください。
import { writeFileSync } from 'node:fs';
const S = 126;
const V = {
'iphone_start.wav': 'アイフォンのバックアップを始めます。アイフォンに、パスコードを入れてください。',
'iphone_done.wav': 'アイフォンのバックアップが、完了しました。',
'iphone_fail.wav': 'アイフォンのバックアップが、失敗しました。調べてください。',
};
for (const [f, t] of Object.entries(V)) {
const q = await (await fetch(`http://127.0.0.1:50021/audio_query?speaker=${S}&text=${encodeURIComponent(t)}`, { method: 'POST' })).json();
const w = Buffer.from(await (await fetch(`http://127.0.0.1:50021/synthesis?speaker=${S}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(q) })).arrayBuffer());
writeFileSync(f, w); console.log(f, w.length);
}
scp iphone_*.wav <ラズパイの名前>:~/notify/sounds/
声が鳴らないときは、ラズパイで aplay ~/notify/sounds/iphone_start.wav を打って、スピーカーから出るかを確認します(cron から鳴らすには、ユーザーが audio のグループに属している必要があります)。
cron には 1 行足すだけです。root の権限は使わず、ふだんのユーザーで動きます。
* * * * * /bin/bash /home/<ユーザー名>/bin/iphone_backup.sh >/dev/null 2>&1
スクリプトの全文は次のとおりです。mkdir -p ~/bin でフォルダーを作り、~/bin/iphone_backup.sh に置いて、chmod 755 ~/bin/iphone_backup.sh で動かせるようにします。
#!/bin/bash
# iPhone をラズパイに挿したら、1 日 1 回バックアップを取って NAS に送る
# cron で 1 分ごとに動かす。つながっていない・信頼していない・今日はもう取った・前の回が動いている、のときは何もしないで終わる。
# 始め:声「パスコードを入れてください」→ iPhone でパスコードを入れると進む
# 終わり:声「完了しました」→ 変更があった分だけ NAS に送る(rsync)
# 失敗:声と LINE で知らせる(notify.ini の iphone_fail)。挿したままなら次は始めない。一度抜いて挿し直すと取り直す
# パスコードを入力せずに放っておいたときは、2 時間で止めて失敗にする
# 手で動かすとき:bash ~/bin/iphone_backup.sh --force(今日もう取っていても取る)
set -u
DIR="$HOME/iphone-backup"
STAMP="$HOME/.iphone-backup-last"
FAILED="$HOME/.iphone-backup-failed" # 失敗した印。iPhone が抜かれたら消す
LOG="$HOME/iphone-backup.log"
NAS_HOST="<NAS の名前>" # ~/.ssh/config に書いた NAS の名前
NAS_DIR="<NAS の置き場所>" # NAS の中のバックアップの置き場所
NOTIFY="/usr/bin/python3 $HOME/notify/notify.py"
# 声は順番待ちに入れたらすぐ鳴らす(1 分ごとの cron を待たない・夜の静かにする時間は playq.py が守る)
say() { $NOTIFY "$@" >/dev/null 2>&1; /usr/bin/python3 "$HOME/notify/playq.py" >/dev/null 2>&1 & }
exec 9>/tmp/iphone-backup.lock
flock -n 9 || exit 0
udid=$(idevice_id -l 2>/dev/null | head -1)
# つながっていない。失敗の印があれば消す(抜いたので、次に挿したら取り直す)
if [ -z "$udid" ]; then
rm -f "$FAILED"
exit 0
fi
# 失敗したあと、挿したままのあいだは始めない(1 分ごとに「失敗しました」が続かないように)
[ -f "$FAILED" ] && [ "${1:-}" != "--force" ] && exit 0
today=$(date +%F)
if [ "${1:-}" != "--force" ] && [ "$(cat "$STAMP" 2>/dev/null)" = "$today" ]; then
exit 0
fi
# 信頼していない iPhone(初めて挿したとき)は取らない。iPhone の「信頼」を押すと、次の 1 分で始まる
idevicepair -u "$udid" validate >/dev/null 2>&1 || exit 0
log() { echo "$(date '+%F %T') $*" >> "$LOG"; }
log "始める $udid"
say iphone_start "iPhone のバックアップを始めます。パスコードを入れてください"
mkdir -p "$DIR"
start=$(date +%s)
out=$(timeout -k 1m 2h idevicebackup2 -u "$udid" backup "$DIR" 2>&1)
rc=$?
echo "$out" | tail -5 >> "$LOG"
if [ $rc -ne 0 ] || ! echo "$out" | grep -q "Backup Successful"; then
log "失敗 番号 $rc"
echo "$udid $(date '+%F %T')" > "$FAILED"
say iphone_fail "iPhone のバックアップが失敗しました(番号 $rc)。一度抜いて挿し直すと取り直します"
exit 1
fi
echo "$today" > "$STAMP"
log "完了 $(( $(date +%s) - start )) 秒 $(du -sh "$DIR" | cut -f1)"
# NAS へ(変更があった分だけ・消えた古いファイルも合わせる)
if rsync -a --delete "$DIR/" "$NAS_HOST:$NAS_DIR/iphone-backup/" >> "$LOG" 2>&1; then
log "NAS へ送った"
say iphone_done "iPhone のバックアップが完了しました"
else
log "NAS へ送れなかった"
say iphone_fail "iPhone のバックアップは取れましたが、NAS へ送れませんでした"
exit 1
fi
この記事を参考に試してみる場合に書きかえる所
- <…> の所:自分の環境に合わせて書きかえる(NAS_HOST は ~/.ssh/config に書いた NAS の名前・鍵でパスワードなしにログインできるようにしておく。NAS_DIR は NAS の中の置き場所)
- say:わたしの環境では、通知の仕組み(notify.py と、声を順番に鳴らす playq.py)を呼んでいます。仕組みはメールを声で知らせる記事に書きました。声だけでよければ、下の 1 行に置きかえれば動きます
say() { aplay -q "$HOME/sounds/$1.wav" >/dev/null 2>&1 & }
どの知らせを声にするか、LINE にも送るかは、通知の設定ファイル(notify.ini)で決めています。失敗だけは気づかないと困るので、声と LINE の両方にしました。
[routes]
iphone_start = voice
iphone_done = voice
iphone_fail = voice, line
[voice]
iphone_start = iphone_start.wav
iphone_done = iphone_done.wav
iphone_fail = iphone_fail.wav
挿して試した結果(2 回目・差分)
- 16:31 挿して 1 分以内に見つけて開始 → 声「パスコードを入れてください」
- パスコードを入力して 2 分半で完了(変更があったのは 163 ファイル。1 回目の 29 分にくらべて、ずっと速い)
- NAS へ送るのに 2 分 → 声「完了しました」
- ラズパイと NAS のファイルの数は、どちらも 35,337 件で一致。温度は 57.5℃ で、今回は熱くならなかった
つまずいた所:声がパスコードのあとに流れた
最初は、声を「順番待ち」に置くだけにしていました。順番待ちは 1 分ごとにまとめて鳴らす作りなので、iPhone にパスコードの画面が出て、入力し終わってから「パスコードを入れてください」が流れてしまいました。順番待ちに置いたあと、すぐに鳴らす処理を呼ぶように直して、挿した直後に流れるようにしました(夜の静かにする時間は、今までどおり鳴らしません)。
つまずいた所:失敗すると 1 分ごとに「失敗しました」が続く作りだった
記事を見直していて気づきました。最初の作りでは、失敗すると「今日の印」を付けずに終わるので、挿したままなら次の 1 分でまた始まり、声と LINE の「失敗しました」が 1 分ごとに続くところでした。失敗したら目印のファイルを置き、iPhone が抜かれるまで次を始めないように直しました。パスコードを入力せずに放っておいたときに待ち続けないよう、timeout -k 1m 2h も付けています。
はじめは timeout 2h だけにしていましたが、パスコードの画面が出ないまま iPhone のケーブルを抜いたとき、idevicebackup2 が止まらずに CPU を使い続け、ラズパイが 18 時間ほど 70℃ 台のままになりました。2 時間の「止めて」の合図(TERM)では止まらなかったため、-k 1m を足して、その 1 分後に強く止める(KILL)ようにしました。前の回が終わらないと次の日のバックアップも始まらないので、ここは付けておくのがおすすめです。
元に戻すとき
idevicebackup2 restore --system --settings ~/iphone-backup
戻す前に、iPhone の設定で「探す」(iPhone を探す)をオフにします。オンのままだと始まりません。暗号化のパスワードを聞かれるので入力します。
SD カードのバックアップがなくなっていたら、NAS から同じ形でラズパイに戻してから打ちます。PC の Apple デバイスのアプリでも戻せます。そのときは、~/iphone-backup の中の、UDID(iPhone の番号)の名前のフォルダーを、PC の MobileSync\Backup の下に置きます。
やってみて
ラズパイでも、PC と同じ丸ごとのバックアップが取れました。PC を起動しなくても、ラズパイに挿すだけで済み、2 回目からは 5 分ほどで終わるのが便利です。
毎回 iPhone のパスコードを入力する必要があるので、寝ているあいだに全部自動で、とはいきません。それでも「挿して、声が聞こえたらパスコードを入力する」だけなので、充電のついでに 1 日 1 回取れるようになりました。