TechQuant Blog

Raspberry Pi で運用していたサービスを VPS に移行した話

7分で読める

SD カードが壊れた。3 度目だ。Raspberry Pi 5 の microSD が突然 read-only に落ち、復旧に半日溶かした金曜の夜、観念して VPS への移行を決めた。

2 年間ラズパイで動かしてきたサービスは 6 つ。Caddy のリバースプロキシ、Vaultwarden、PhotoPrism、Tandoor、自前の家計簿 API、それと小さな Web ツール群。トラフィックは月 2 万 PV ほど。電気代込みで月 500 円くらいの計算でずっと回していた。

移行を済ませて 2 週間。今のところ後悔はない。むしろ「もっと早くやればよかった」と思っている。判断材料と手順を残しておく。同じ温度感で迷っている人の役に立つかもしれない。

ラズパイ運用で詰んだ瞬間

故障の主因は、ほぼ間違いなく書き込みの偏り。PhotoPrism がサムネイルを再生成するたびに SD カードに大量書き込みが走るのは把握していたし、SSD への boot 移行も計画にはあった。けど先延ばしにしていた。ツケが回ってきた、というだけの話。

痛かったのは、復旧コストよりも「いつ次が来るか分からない」という不安だった。家族のパスワードボルトもラズパイ上に乗っていた。Vaultwarden が朝起きて死んでいたら、シャワー前にパスワードを取り出せない。これはまずい。

もう一つ、夏場の挙動も無視できなかった。室温 32 度を超えると Raspberry Pi 5 はサーマルスロットリングで体感半分の速度に落ちる。以前まとめたラズパイ運用Tipsに書いた通り、ヒートシンクを盛って多少は改善したが、根本対策ではなかった。

VPS に移すか、自宅でハードウェアを強化するか

正直、最初は中古の小型 NAS に乗せ替えるか、N100 のミニ PC を買う案も真剣に検討した。電気代と初期費用の差し引きを ChatGPT に計算させてみたら、3 年スパンでは自宅機の方が安い。

でも結局 VPS にした。理由は 3 つ。

  • 停電や回線断のときに死ぬのが嫌だった(去年の台風で 4 時間落ちた)
  • 固定 IP 取得とポート開放まわりの作業に飽きた
  • 引っ越しの予定があり、自宅機を運ぶオペレーションを未来の自分に課したくなかった

金額勝負ではなく、運用負荷の勝負だった、というのが今振り返っての感想。月 1,000 円くらいまでなら払う、と腹を決めたら選択肢が一気に絞れた。VPS は お名前.com の VPS を契約した。2GB プランで月 968 円、SSD 100GB、東京リージョン。決め手は最低利用期間の縛りが緩いことと、コンパネからスナップショットがワンクリックで取れること。

移行の流れ

大筋は「Docker Compose のままごっそり持っていく」。ラズパイ時代から全サービスを compose ファイル管理にしていたのが、ここで効いた。

1. データのバックアップと圧縮

# volumes ディレクトリをまるごと固める
sudo systemctl stop docker
sudo tar -czf /tmp/services.tar.gz -C /srv volumes/
ls -lh /tmp/services.tar.gz   # 4.2GB

PhotoPrism の写真本体は別系統で S3 互換ストレージに置いていたので、ここでは Vaultwarden の SQLite と Tandoor の Postgres ダンプ、Caddy の autosave 証明書だけが対象になった。

2. VPS 側の下準備

Ubuntu 24.04 LTS を選んで初期化。最低限やったのは以下。

  • 非 root ユーザー作成と SSH 鍵認証化、パスワード認証 OFF
  • ufw で 22, 80, 443 のみ許可
  • Docker と Docker Compose プラグインの導入
  • fail2ban の jail 設定(sshd と nginx-botsearch)
  • サーバー時刻を JST に変更(cron 系の整合のため)

このへんは雑にやると後で泣くので、Ansible Playbook 化しておいた。次回の移行時はこれを流すだけで終わる。

3. データを転送して compose up

# ラズパイ側から VPS へ rsync
rsync -avz --progress /tmp/services.tar.gz \
    user@vps.example.com:/srv/

# VPS 側で展開して docker compose
ssh user@vps.example.com
sudo tar -xzf /srv/services.tar.gz -C /srv/
cd /srv/services
docker compose up -d

ここで一発で全部上がる、と期待していたら、案の定 Caddy の自動 SSL でつまずいた。新サーバーの IP に DNS が向いていないため、Let's Encrypt のチャレンジが通らない。当たり前といえば当たり前。

4. DNS の切り替えと TLS の再取得

段階的にやった。まず DNS の TTL を 60 秒に下げて、ラズパイ側を生かしたまま VPS の IP に向けて切り替え。Caddy が /data/caddy/certificates/ 配下に新しい証明書を取りにいくのを journalctl -u caddy -f で見守った。Caddy のリバースプロキシ設定を整理しておいたおかげで、設定ファイルはそのままコピーで動いた。

移行後の体感

PhotoPrism のインデックス再構築が、ラズパイ時代に 47 分かかっていたのが VPS で 8 分に短縮された。サムネイル生成中も他のサービスがもたつかなくなった。これだけで月 1,000 円の価値はある、と一瞬で納得した。

家計簿 API のレスポンスは中央値で 180ms → 65ms。CPU が 4 コアの x86 になったというより、SD カードのランダム I/O が消えた効果が大きい気がしている。ベンチマークを正しく取ったわけではないので、ここは感覚値。

意外な副作用として、宅内ネットワークがやや軽くなった。Tailscale 経由で外から自宅にアクセスする頻度が減ったので、上り帯域の取り合いがなくなった。Tailscale を使った自宅 VPN 構成はそのまま残してあるが、用途が「メディアサーバーアクセス」だけに絞られた。

ハマりどころメモ

移行で詰まった点を、未来の自分のために書き残しておく。

  • UID/GID のズレ: ラズパイの pi ユーザーは UID 1000、VPS の新規ユーザーも UID 1000 だったので volumes はそのまま読めた。けど Tandoor のように内部で固定 UID を使うアプリはここでパーミッションエラー祭りになる。chown -R で逃げた。
  • aarch64 → x86_64 の Docker イメージ: ほぼ全部マルチアーキで配布されていて影響なかったが、自前ビルドした 1 個だけ作り直しが必要だった。Dockerfile を読み返すきっかけになった、と前向きに捉えている。
  • cron の方言: ラズパイで動かしていた一部のシェルスクリプトを移すついでに、systemd timer に切り替えた。前にまとめた cron→systemd timer の移行手順がそのまま使えた。
  • ログ送信先のハードコード: ntfy のトピック URL や Discord Webhook を ~/.bashrc に書いていたものがあって、そのまま動かなくて 30 分ハマった。設定の場所はちゃんと一箇所に集約しておくべき、という当たり前の教訓。

ラズパイをどう使うか、再考した

ラズパイは捨てなかった。今は宅内 LAN 専用で、Pi-hole と Home Assistant、それと SwitchBot ハブだけを動かしている。WAN に晒さない用途に絞ったので、SD カード書き込みもログ最小化で 1/10 程度に落ちた。役割を絞れば、ラズパイは今でも頼れる。

結論じみたものを書くなら、こうなる。外から触られる用途は VPS、家の中で完結する用途はラズパイ。境界をはっきり決めたら、運用の頭の中もスッキリした。次の引っ越しのときに自宅機を箱詰めしなくてよくなったのも、地味に効いている。

同じ判断で迷っている人がいたら、SD カードが壊れる前に動いた方がいい、とだけ伝えておきたい。3 度目で気付いた身としては、それくらいしか言えることがない。