おはようございます。まさきんです。
Obsidian のノート一式を、Google Drive へ毎日退避する形にしました。Mac 標準の rsync で写すだけのつもりが、日本語の長いファイル名で一度落ちています。同じところで止まる人がいそうなので、手順と回避策をメモしておきます。
※記事公開時点の情報です。最新の条件は公式サイトでご確認ください。
- Obsidian のバックアップは、ボルトを rsync で Google Drive へ写すだけで組める
- macOS 標準の rsync(openrsync)は、長い日本語ファイル名で一時ファイルを作れずに落ちた
--inplaceを付けると一時ファイルを作らなくなり、僕の環境では通った- 実行は launchd で1日2回。初回は約2万ファイル・数GB規模で、終了コードは0だった
- 秘密情報とキャッシュは対象から外し、クラウド側の確認は週1回にした
なぜ退避先をもう一つ増やしたか
Obsidian のノート一式は「ボルト」と呼ばれます。僕のボルトは、これまで GitHub に退避していました。ただ、退避先が一つだけなのは少し心もとないと感じていました。
そこで、もう一つの置き場所として Google Drive を選びました。パソコン版 Google ドライブのマイドライブ配下に、フォルダを一つ用意します。そこへ rsync で増分を写す形です。
先日、Obsidian Sync を解約した話を書きました。メモの正本は Notion に移していますが、ボルトのファイル自体は手元に残っています。同期をやめたぶん、退避は自分で持っておきたいと考えました。
ボルトはただのフォルダ
Obsidian 公式ヘルプでは、ノートは Markdown 形式のテキストファイルとして保存されるとあります。ボルトは、ローカルのファイルシステム上のフォルダです。サブフォルダも含まれます。
設定は、ボルト直下の .obsidian フォルダに入ります。ホットキーやテーマなどが、ここに置かれるという説明です。
つまり、バックアップに特別な道具は要りません。フォルダを別の場所へ写せば、それが退避になります。rsync が使えるのは、この作りのおかげですね。
同期とバックアップは別もの
Obsidian 公式ヘルプは、同期とバックアップを分けて説明しています。同期はノートを最新に保ちます。ただ、データの消失からは守ってくれないという案内です。
| 項目 | 同期 | バックアップ(退避) |
|---|---|---|
| 目的 | どの端末でも同じ状態にする | 別の場所にコピーを残す |
| 向き | 双方向が基本 | 元から退避先への一方向 |
| 消したとき | 消した結果もほかへ伝わる | 設定しだいで退避先に残る |
| 壊れたとき | 壊れた状態も伝わりうる | 伝わる前の版が残る可能性がある |
| 今回の役割 | 使わない | rsync で1日2回写す |
今回やっているのは、右側の退避です。Google Drive のフォルダを Obsidian で開くことはしません。あくまで写した先として置いています。
公式ヘルプには、同じボルトを複数のサービスで同期しないようにとの注意もあります。データの競合や破損を避けるためです。退避先を直接編集しないのは、この注意とも合う形だと思います。
全体の構成
仕組みは3つの部品だけです。
- ボルトのフォルダ(例:
~/Notes) - Google Drive のマイドライブ配下の退避フォルダ
- rsync を呼ぶシェルスクリプトと、それを定時に動かす launchd
rsync がローカルのフォルダへ写します。そこから先のアップロードは、パソコン版 Google ドライブの仕事です。スクリプトはクラウドの API を呼びません。
ミラーリングとストリーミングの違い
パソコン版 Google ドライブには、マイドライブの扱い方が2つあります。公式ヘルプの説明を表にまとめました。
| 項目 | ストリーミング | ミラーリング |
|---|---|---|
| ファイルの保存場所 | 主にクラウド | クラウドとローカルの両方 |
| オフライン利用 | オフライン指定したものだけ | いつでも使える |
| パソコンの容量 | ほとんど使わない | 完全なコピーの分だけ使う |
| macOS での場所 | ~/Library/CloudStorage 配下(変更不可) | 設定で選んだフォルダ |
macOS のストリーミングは、Finder サイドバーの「場所」に表示されます。既定の位置は ~/Library/CloudStorage で、変更できないとされています。
rsync の写し先として考えると、整理はこうなりそうです。ミラーリングは、写した結果が手元にも丸ごと残ります。そのぶん、ボルトと退避の二重で容量を使います。ストリーミングは容量を節約できますが、ファイルの実体は主にクラウド側です。
どちらが正解かは、ディスクの空きしだいだと思います。数GB規模なら、ミラーリングの負担は小さいかもしれません。なお Obsidian 公式ヘルプには、Google Drive に置くボルトの案内があります。オフラインで使える設定にする、という内容です。これは同期の用途の話ですが、考え方は参考になりそうです。
rsync の基本コマンド
骨格はこのくらいです。パスは一般化した例にしています。
#!/bin/bash
SRC="$HOME/Notes/"
DEST="$HOME/GoogleDrive/vault-backup/"
rsync -a --inplace \
--exclude '.venv/' \
--exclude 'node_modules/' \
--exclude '.cache/' \
--exclude 'tmp/' \
--exclude '.env' \
--exclude '*.token' \
"$SRC" "$DEST"
rc=$?
echo "$(date '+%F %T') rc=$rc" >> "$HOME/Library/Logs/vaultbackup.log"
exit $rc
DEST は、自分のマイドライブ配下の実際のパスに置き換えます。ストリーミングなら ~/Library/CloudStorage の下になります。
使っているオプションは少なめです。
| オプション | 意味(man ページの説明) | 今回の使い方 |
|---|---|---|
-a | 再帰や時刻の保持などをまとめて指定 | 付ける |
--inplace | 一時ファイルを作らず、写し先に直接書く | 付ける(後述の回避策) |
--exclude | パターンに合うファイルを除く | 秘密情報やキャッシュを除く |
-n | 写し先を実際には変えない試し実行 | 初回の前に確認用で使う |
--delete | 元に無いファイルを写し先から消す | 付けるかは方針しだい |
--delete を付けると、元で消したファイルが退避先からも消えます。動きは同期に近づきます。誤って消したノートを残したいなら、付けない選択もあると思います。
写し元の末尾のスラッシュにも注意が要ります。付けると「フォルダの中身」を写します。付けないと、フォルダごと一段深く入ります。
退避から外すものの線引き
ボルトの中には、ノート以外のものも混ざりがちです。僕は次の4種類を対象から外しました。
| 種類 | 例 | 外す理由 |
|---|---|---|
| 仮想環境 | .venv/ や node_modules/ | 作り直せる。ファイル数が多い |
| キャッシュ | .cache/ など | 作り直せる。中身が頻繁に変わる |
| 一時置き場 | tmp/ など | 残す必要がない |
| 秘密情報 | .env やトークンのファイル | クラウドに置く範囲を広げたくない |
判断の軸は2つだと思います。作り直せるものは外す。漏れて困るものも外す。この2つです。
秘密情報は、退避先が増えるほど置き場所も増えます。なので、最初から対象に入れない形にしました。秘密情報の探し方は、gitleaks で「no leaks」でも漏れた2件の話にも書いています。
除外の指定は、-n を付けた試し実行で確かめられます。-v も足すと、写す予定のファイルが一覧で出ます。秘密のファイル名が出てこないかを見ておくと安心ですね。
押すと楽天のログインページが開きます。ログイン後にキャンペーン内容が表示されます。
つまずき:日本語の長いファイル名で落ちた
最初の実行は、途中で止まりました。原因は、長い日本語ファイル名のファイルです。
エラーに出ていたのは mkstempat という関数名でした。一時ファイルの作成に失敗した、という内容です。一時ファイルの名前が255バイトを超えていたのが原因でした。
仕組みはこうです。rsync はふつう、写し先に一時ファイルを作ります。転送が終わったら、本来の名前へ移します。samba.org の rsync の man ページにも、この既定の動きが書かれています。
一時ファイルの名前は、元の名前より長くなります。元の名前がぎりぎり収まる長さでも、一時ファイルの名前は上限を超えることがあるわけです。
ファイル自体は存在できているのに、写すときだけ失敗します。元のファイル名は上限内でも、一時ファイルの名前が上限を超えるためです。ノートの題名を長めの日本語で付けている人は、踏みやすいかもしれません。
255バイトは日本語で何文字か
macOS のヘッダでは、NAME_MAX が255と定義されています。コメントは「ファイル名の最大バイト数」です。
ここからは計算です。日本語の多くの文字は、UTF-8 で1文字3バイトになります。255を3で割ると85です。つまり、日本語だけの名前なら約85文字が目安になります。拡張子や半角文字が混ざると、数字は少し変わります。
一方で、話は少し複雑です。Apple の開発者フォーラムでは、Apple のサポート担当者が別の説明もしています。APFS 自体の上限は255文字で、NAME_MAX とは合わないという内容です。上限はボリュームの形式で変わる、とも書かれていました。
なので、僕は「約85文字」を安全側の目安として扱っています。写し先のフォルダがどの仕組みの上にあるかで、効く上限は変わるかもしれません。
回避策は —inplace
対処は、オプションを一つ足すだけでした。--inplace です。
openrsync の man ページでは、一時ファイルを作らずに写し先のファイルを直接操作すると説明されています。一時ファイルを作らないので、名前が長くなる問題も起きません。僕の環境では、これで最後まで通りました。
ただ、引き換えになるものもあります。samba.org の man ページが挙げている注意点はこのあたりです。
- 転送中は、写し先のファイルが中途半端な状態になる
- 途中で止まると、その中途半端な状態が残りうる
- ほかの人が開いているファイルの更新には使わない
今回の写し先は、誰も開かない退避フォルダです。止まっても、次の実行で写し直されます。なので、この用途なら受け入れられる引き換えだと判断しました。
ファイル名のほうを短くする手もあります。とはいえ、約2万ファイルの名前を直すのは現実的ではありませんでした。
macOS の rsync は openrsync
そもそも、Mac の rsync は以前と中身が違います。近年の macOS では、標準の rsync が openrsync という実装になっています。
Apple は rsync のソースを公開しています。そのリポジトリには openrsync のディレクトリが含まれています。man ページの名前も「openrsync」です。samba.org 版のプロトコル27〜29と互換がある、と説明されています。
切り替わった時期は、macOS Sequoia 15.4 前後とされています。ただ、Apple の公式リリースノートでは確認できませんでした。なので、ここは「とされています」に留めます。
実務上の注意は一つです。ネットで見つけた rsync の例が、そのまま動くとは限りません。使うオプションは、手元で man rsync を引いて確かめるのがよさそうです。
launchd で1日2回動かす
定時実行は launchd に任せました。Apple の開発者向け文書では、cron はまだ使えるものの推奨されていません。launchd を使うよう案内されています。
時刻の指定は StartCalendarInterval というキーです。plist の骨格はこうなります。ジョブ名と時刻は例です。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.vaultbackup</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/yourname/bin/vaultbackup.sh</string>
</array>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key><integer>3</integer>
<key>Minute</key><integer>30</integer>
</dict>
<dict>
<key>Hour</key><integer>13</integer>
<key>Minute</key><integer>30</integer>
</dict>
</array>
<key>StandardOutPath</key>
<string>/tmp/vaultbackup.out.log</string>
<key>StandardErrorPath</key>
<string>/tmp/vaultbackup.err.log</string>
</dict>
</plist>
launchd.plist の man ページによると、時刻の辞書は複数並べられます。1日2回なら、辞書を2つ書く形です。書かなかった項目は「すべて」の扱いになります。
僕は未明と昼過ぎの2回にしました。未明は前日の分を拾う役です。昼過ぎは、午前の作業を拾う役になります。
スリープ中の扱いは cron と違います。Apple の文書では、予定の時刻にスリープしていた場合は復帰したときに実行されるとあります。
cron の書き方に慣れている人は、cron の日と曜日の罠の記事もあわせてどうぞ。定時実行は、書いたあとの検算が大事だと思っています。
初回と定時の結果
初回は、約2万ファイル・数GB規模でした。--inplace を付けたあとの初回は、正常に終わっています。終了コードは0でした。
launchd からの定時の実行も、同じく0で終わりました。手で動かしたときと、定時で動いたとき。両方を確かめたのは、動く環境が少し違うからです。
ただ、これは「ローカルの退避フォルダへ写せた」という意味です。クラウドへのアップロードは、別の話になります。初回のアップロードは時間がかかっています。この記事の時点では、完了をまだ確認できていません。なので、その結果は書きません。
クラウド側の確認は週1回
rsync の終了コードが0でも、クラウドに届いたとは限りません。なので、届いているかの確認を別に置きました。頻度は週1回だけです。
見るのは2点です。
- 新しさ:クラウド側のファイルが最近の日付になっているか
- 中身:ファイルを実際に開いて読めるか
日付だけなら、壊れたファイルでも新しく見えます。開いて読むところまでやって、初めて「戻せる退避」と言えるのではないでしょうか。
毎日やらないのは、続かないからです。ここは週次で十分だと考えました。
失敗の通知はスクリプトに持たせない
失敗したときの通知は、スクリプトからは送りません。既にある見張りの仕組みに任せる設計にしました。
スクリプトの役目は2つだけです。写すこと。終了コードをログに残すこと。この2つです。
通知まで入れると、通知先の設定や秘密情報がスクリプトに入り込みます。見張りを一か所にまとめておけば、ジョブが増えても直す場所は一つです。
Time Machine を見送った理由
Mac なら Time Machine も候補になります。Obsidian 公式ヘルプも、OS の道具として挙げています。
ただ、僕の Mac では保存先が未設定でした。そのままでは使えません。Apple のサポートページによると、外付けのストレージが必要です。容量は Mac の2倍以上が目安とされています。
今回はそこまで用意せず、見送りました。Time Machine は Mac 全体を守る道具です。ボルトだけを別の場所へ逃がす今回の目的とは、少し役割が違うのかもしれません。
よくある失敗と落とし穴
組んでみて、つまずきやすいと感じた点を並べます。
- 長い日本語ファイル名で、一時ファイルが作れずに落ちる
- ネットの rsync の例を、openrsync でそのまま使おうとする
- 写し元の末尾のスラッシュを付け忘れ、一段深く写してしまう
.envやトークンを除外せず、クラウドに載せてしまう- 終了コード0を見て、クラウドにも届いたと思い込む
- 退避先のフォルダを Obsidian で開いて編集してしまう
僕が実際に踏んだのは、一つ目です。五つ目は、週1回の確認で防ぐつもりでいます。
始める前に確認すること
- ボルトのフォルダの場所と、おおよそのファイル数・容量
- Google Drive の空き容量と、パソコンのディスクの空き
- マイドライブがストリーミングかミラーリングか
- ボルトの中に
.envやトークンのファイルが無いか - 日本語で80文字を超えるような長いファイル名が無いか
-nの試し実行で、写す対象が意図どおりか
よくある質問
Q. Obsidian のバックアップは Google Drive だけで足りますか?
一か所だけだと、そこが使えないときに困ります。Obsidian 公式ヘルプも、クラウドは物理的な保存の代わりであり補完でもあると説明しています。僕は GitHub との二か所にしました。
Q. Google Drive に置いたボルトを直接開いてもよいですか?
Obsidian 公式ヘルプでは、Google Drive での同期は公式にはサポートされていないとされています。今回の構成でも、退避先は開かない前提です。編集は元のボルトだけで行うのが安全だと思います。
Q. --inplace は常に付けてよいですか?
用途によります。転送中は写し先が中途半端な状態になる、という注意が man ページにあります。誰も開かない退避先なら、影響は小さいと言えそうです。
Q. Homebrew の rsync を入れる方法はどうですか?
samba.org 版の rsync を別に入れる方法もあります。ただ、僕は試していません。今回は --inplace を足す形で対処しました。
Q. 1日2回は多すぎませんか?
rsync は差分だけを写します。変更が無ければ、ほとんど何もせずに終わります。回数を増やしても負担は小さいので、失ってもよい時間の幅で決めるのがよいかもしれません。
Q. Mac がスリープしていたらどうなりますか?
Apple の文書では、スリープ中に時刻を過ぎたジョブは復帰したときに実行されます。電源が切れていた場合の扱いは、別途確認が要ります。
Q. 元でノートを消したら、退避先からも消えますか?
--delete を付けていなければ、退避先には残ります。付けると、元に無いファイルは退避先からも消えます。どちらにするかは、退避に何を期待するかで決まります。
Q. アップロードが終わったかはどこで見ますか?
パソコン版 Google ドライブのアプリで、同期の状況を確認できます。僕は初回のアップロードの完了を、まだ確認できていません。週1回の確認で、開いて読めるところまで見る予定です。
参考にした公式情報
- How Obsidian stores data(Obsidian Help)
- Back up your Obsidian files(Obsidian Help)
- Sync your notes across devices(Obsidian Help)
- パソコン版ドライブを使用する(Google ドライブ ヘルプ)
- ファイルのストリーミングとミラーリング(Google ドライブ ヘルプ)
- macOS でパソコン版ドライブを使用する(Google ドライブ ヘルプ)
- rsync(1) man ページ(samba.org)
- rsync のソース(Apple OSS Distributions)
- Scheduling Timed Jobs(Apple Developer)
- How to query APFS file name size limit?(Apple Developer Forums)
- Time Machine で Mac をバックアップする(Apple サポート)
まとめ
Obsidian のボルトは、ただのフォルダです。なので、rsync と launchd だけで毎日の退避が組めました。
つまずいたのは、日本語の長いファイル名でした。--inplace を一つ足せば通りますが、引き換えがあることは知っておきたいところです。
クラウドに届いたかの確認は、これからです。まずは -n の試し実行から、手元のボルトで試してみてはいかがでしょうか。
バックアップのような「毎月の仕組み」を整えたら、毎月の通信費も同じ目で見直せるかもしれません。
本記事にはアフィリエイト広告を含みます。当サイトのリンクを経由して商品・サービスへお申し込みいただいた場合、提携先企業から成果報酬を受け取ることがあります。 また、運営者は楽天グループに所属する社員であり、紹介プログラムを通じた報酬を受け取る場合があります。記事内容・評価は広告の有無にかかわらず、運営者の実体験と調査に基づき作成していますが、 上記の関係性をご理解のうえお読みください。詳しくは免責事項・アフィリエイト表記をご覧ください。
楽天モバイルの通信エリア・電波状況について気になることがある方は、公式の電波改善・調査依頼フォームからご相談いただけます。