Overlayfsでストレージの延命と電源遮断の安全性を確保しながら、データ永続化を図る

一般的なRaspberry PiはmicroSDカードにOSを書き込んで利用します。軽便ですが破損しやすいストレージメディアでもあります。CM5など産業用途でeMMCが採用されている理由の1つは、microSDカードより堅牢なストレージだからです。

microSDカードなどの延命を図る目的で、OS部分を読み取り専用(Read Only)にすると、ストレージへの書き込みを大幅に削減ができてストレージメディアの延命を図れます。加えて、突然の電源遮断があってもOS部分の破損を防ぐ確率が高まります。

Raspberry Pi にはraspi-configから簡単に設定できる「Overlay File System」という手法があります。だいぶ以前からraspi-configから設定できるようになっています。

OverlayFSは、システム(OS)を読み取り専用(RO)として扱い、書き込みが発生したファイルだけを別の書き込み可能な領域(上層)へコピーして保存することで、全体を1つのファイルシステムのように見せる仕組みです。

この書き込み可能な領域は、tmpfsとしてメモリー上に展開されるため、電源を入れ直すと消えてしまいます。
一見すると変更&保存ができたように扱えても、システム部分が読み取り専用化されていて変更はできていません。
だから、電源を切れば何も無くなります。
これはシステムを読み取り専用化した時点に戻るというわけです。

最低限のファイルだけは再起動後も保持して使いたいですよね。
Overlayfsを利用しながら、別パーティションにシンボリックリンクでデータを退避させて実現します。
今回はネットワーク関連の変更と、homeフォルダの保存だけに絞って設定しました。

今回のテスト環境

今回試した環境は次の通りです。

  • 32GBのmicroSDカード(64GB以上を推奨)
  • Ubuntu Desktop 25.10(64bit)
  • /dataを別パーティションに分割
  • シンボリックリンク(symlink)で/dataへ退避
  • ネットワークはNetworkManagerを使用
  • OverlayFSは手動で実施、cmdline.txtの追記
  • テストはPi 5実機(本手法はCM5を搭載したPL-R5でも同様に適用可能です)

microSDカードの分割は/data領域を10GBで試しています。

パーティションの分割方法は、「OSがインストールされているパーティションの分割方法」をご覧ください。

退避用にパーティションを分割したUbuntuを起動したら、アップデートを済ませておきます。
この状態で次からの手順を進めていきます。

設定の流れ

設定していく大まかな流れは次の通りです。

1.Ubuntuの書き込み
2.実機で起動し、初期セットアップ&アップデートを済ます。
3.一度microSDカードを、別のマシンで読み込ませパーティションを分割する(データ退避用10GB程度)
4.Overlayfsにする前の準備作業
5.Overlayfsを有効

3まで終わったので、最終的にOverlayfsにする準備を続けます。
最後にOverlayfsが有効になった状態で、ネットワーク設定の変更などで確認します。

別パーティションをマウント

分割したパーティションをマウントさせてから作業していきます。
lsblkで調べると、分割したパーティションはまだマウントされていません。(mmcblk0p3

NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
mmcblk0     179:0    0  29.7G  0 disk 
├─mmcblk0p1 179:1    0   512M  0 part /boot/firmware
├─mmcblk0p2 179:2    0  19.9G  0 part /
└─mmcblk0p3 179:3    0   9.3G  0 part 

ルート配下に/dataを作成し、そこにmmcblk0p3をマウントさせます。

sudo mkdir -p /data
sudo mount /dev/mmcblk0p3 /data

└─mmcblk0p3 179:3 0 9.3G 0 part /dataとなればOKです。

起動時に自動マウントさせるため、fstabに追記します。blkidもしくは次のコマンでUUIDを調べる。

sudo blkid -s UUID -o value /dev/mmcblk0p3

または

lsblk -f /dev/mmcblk0

UUIDをコピーしておいてから、sudo nano /etc/fstabでfstabに追記。(UUIDは読み替えてください)

UUID=f3afb6b2-a959-4c4b-bdb6-3c38a0fe33be /data ext4 defaults 0 2

更新して再マウントをし、mount状態を確認しておきます。

sudo systemctl daemon-reload
sudo mount -a
mount | grep /data

/dev/mmcblk0p3 on /data type ext4 (rw,relatime,errors=remount-ro)と表示されればOKです。

/data側の役割

最終的には/dataは書き込み可能(rw)な状態にしますので、この/data領域へネットワークの設定を移します。
合わせて/homeディレクトリも/data領域に保存させます。

システムのrootfsは最後に読み取り専用(ro)であるOverlayfsにします。

この時、データを退避する方法として、大まかに2つあります。
シンボリックリンク(symlink)の方法と、バインドマウント(bind mount)の方法です。

今回はシンボリックリンクです。

Overlayfs+シンボリックリンク

シンボリックリンクでの作業の流れは次ようになります。

1.リンボリックリンク先としてディレクトリを作成しマウントポイントとする。
2.元のファイルをコピーしておく。
3.シンボリックリンクの作成をする
4.シンボリックリンクの確認をする

シンボリックリンクがきちんと張れた後でOverlayfs化していきます。

1. マウントポイント用ディレクトリの作成

シンボリックリンクの実行の前に空のディレクトリだけ用意します。マウントポイントです。

今回はシンボリックリンクするのは次の5つです。

  • /etc/NetworkManager
  • /var/lib/NetworkManager
  • /home
  • /etc/hostname
  • /etc/hosts
  • /etc/netplan /dataにマウント用のディレクトリを作成を作成しておきます。
sudo mkdir -p /data/etc/NetworkManager
sudo mkdir -p /data/var/lib/NetworkManager
sudo mkdir -p /data/home
sudo mkdir -p /data/etc/netplan

2.ファイルのコピー

元のデータを/data側へコピーしておきます。
コマンドでのディレクトリの指定は、今回のケースだと既にあるディレクトリ内に中身だけをコピーしたいため、最後にスラッシュを入れます。
この辺は間違うと、ディレクトリの入れ子状態になって詰みます。

•「中身を入れる」なら末尾スラッシュ
•「フォルダごと作る」なら無し

# NetworkManagerの設定をコピー
sudo rsync -a /etc/NetworkManager/ /data/etc/NetworkManager/
sudo rsync -a /var/lib/NetworkManager/ /data/var/lib/NetworkManager/

# netplanの設定もコピー
sudo rsync -a /etc/netplan/ /data/etc/netplan/

# homeディレクトリをコピー
sudo rsync -a /home/ /data/home/

# 設定ファイルのコピー
sudo cp -a /etc/hosts /data/etc/hosts
sudo cp -a /etc/hostname /data/etc/hostname

hostsとhostnameは1つのファイルです。だからcp -aコマンドでも問題ありません。
※netplan/はWi-Fi接続情報があります。

3.シンボリックリンクの作成

シンボリックリンクを作成する前に元のファイル群を削除してからシンボリックリンクにします。

sudo rm -rf /etc/NetworkManager
sudo ln -s /data/etc/NetworkManager /etc/NetworkManager

sudo rm -rf /var/lib/NetworkManager
sudo ln -s /data/var/lib/NetworkManager /var/lib/NetworkManager

sudo rm -rf /etc/netplan
sudo ln -s /data/etc/netplan /etc/netplan

# homeはルートに移動してから
cd /
sudo rm -rf /home
sudo ln -s /data/home /home

sudo ln -sf /data/etc/hosts /etc/hosts
sudo ln -sf /data/etc/hostname /etc/hostname

ここで一度確認しておきます。

ls -l /etc/NetworkManager
ls -l /var/lib/NetworkManager
ls -l /etc/netplan
ls -l /home
ls -l /etc/hostname
ls -l /etc/hosts

例えば、次のような表示になるハズです。

lrwxrwxrwx 1 root root 24 Feb  7 21:08 /etc/NetworkManager -> /data/etc/NetworkManager
lrwxrwxrwx 1 root root 28 Feb  7 21:09 /var/lib/NetworkManager -> /data/var/lib/NetworkManager
lrwxrwxrwx 1 root root 17 Feb  9 09:40 /etc/netplan -> /data/etc/netplan
lrwxrwxrwx 1 root root 10 Feb  7 21:09 /home -> /data/home
lrwxrwxrwx 1 root root 18 Feb  7 21:10 /etc/hostname -> /data/etc/hostname
lrwxrwxrwx 1 root root 15 Feb  7 21:10 /etc/hosts -> /data/etc/hosts

シンボリックリンクでNetworkManagerなどが/data側を使っているかの確認をします。

realpath /etc/NetworkManager
realpath /etc/NetworkManager
realpath /var/lib/NetworkManager

結果、realpathコマンドでも/data側のパスが表示されればOKです。

/data/etc/NetworkManager
/data/var/lib/NetworkManager

マウントした/dataパーティションは最終的にはこんな感じです。

書き込み可能な/data側にコピーができている状態です。

4.Overlayfsのインストールと更新

ここまでで準備は整いました。
overlayrootをインストールして、initramfsを更新します。

1.Overlayfsのインストール(overlayroot)
2.cmdline.txtに追記
3.Overlayfsの状態でネットワークの設定を変更し恒久的か確認する

sudo apt update
sudo apt install overlayroot
sudo update-initramfs -u
sudo reboot

initramfsは、更新が完了したらこのような表示がされます。

update-initramfs: Generating /boot/initrd.img-6.17.0-1007-raspi
Using DTB: bcm2712-rpi-5-b.dtb
Installing /lib/firmware/6.17.0-1007-raspi/device-tree/broadcom/bcm2712-rpi-5-b.dtb into /boot/dtbs/6.17.0-1007-raspi/./bcm2712-rpi-5-b.dtb
Taking backup of bcm2712-rpi-5-b.dtb.
Installing new bcm2712-rpi-5-b.dtb.
flash-kernel: installing version 6.17.0-1007-raspi
Copying kernel assets to /boot/firmware/new/
Copying boot firmware to /boot/firmware/new/
Copying device trees to /boot/firmware/new/
Copying device tree overlays to /boot/firmware/new/overlays/
Please be aware next reboot will boot twice

5.cmdline.txtへ追記

再起動から戻ったら、/dataだけはrwのままにしておきたいので、別ブロックを除外するオプションを付けて追記します。
cmdline.txtのパスはRaspberry Pi OSと異なります。

sudo nano /boot/firmware/current/cmdline.txt

cmdline.txtの先頭に追記する。
注意:cmdline.txtは1行で書かないとなりません。改行はしません。項目間はスペースで空けます。

overlayroot=tmpfs:recurse=0
overlayroot=tmpfs:recurse=0 zswap.enabled=1 zswap.compressor=zstd multipath=off dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 panic=10 rootwait fixrtc quiet splash

記述したら再起動します。

これで設定はすべて終わりです。
bootfsだけが読み取り専用(ro)になったのか、再起動して確かめましょう。

読み取り専用と書き込み可能を確認してみる

再起動後は、きちんとrootfs側のOverlayfsが有効なのか確認しておきます。
逆に/dataは書き込み可能かも確認します。

先ずlsblkの結果は、ちゃんとrootfsがOverlayfsになっています。(/media/root-ro)
/dataはそのままなのでrwです。

mmcblk0     179:0    0  29.7G  0 disk 
├─mmcblk0p1 179:1    0   512M  0 part /boot/firmware
├─mmcblk0p2 179:2    0  19.9G  0 part /media/root-ro
└─mmcblk0p3 179:3    0   9.3G  0 part /data

ターミナルでSSH接続すると、接続時に表示されるので、よく分かります。overlayの表記がありますね。

tmpfs-root /media/root-rw tmpfs rw,relatime,inode64 0 0
overlayroot / overlay rw,relatime,lowerdir=/media/root-ro,upperdir=/media/root-rw/overlay,workdir=/media/root-rw/overlay-workdir/_,uuid=on,nouserxattr 0 0
/dev/mmcblk0p2 /media/root-ro ext4 ro,relatime 0 0

個別に確認をしていきます。

mount | grep " on / "
mount | grep /data
ls -l /etc | grep NetworkManager
ls -l / | grep /home

期待される結果:

mount | grep " on / "
overlayroot on / type overlay (rw,relatime,lowerdir=/media/root-ro,upperdir=/media/root-rw/overlay,workdir=/media/root-rw/overlay-workdir/_,uuid=on,nouserxattr)

mount | grep /data
/dev/mmcblk0p3 on /data type ext4 (rw,relatime)

ls -l /etc | grep NetworkManager
lrwxrwxrwx 1 root                 root                    24 Feb  7 21:08 NetworkManager -> /data/etc/NetworkManager

ls -l / | grep /home
lrwxrwxrwx   1 root    root            10 Feb  7 21:09 home -> /data/home

期待される結果が表示されましたか? これでOverlayfsは完了です。

最終テストをしてみる

2つほどテストしてみます。
 

Wi-Fi接続先の変更

Overlayfs状態でもWi-Fiの接続先を変更し、それが再起動後も反映するのか検証してみます。

Wi-Fiの接続情報は、/etc/netplan内にyaml形式で存在しています。
これもシンボリックリンクを張ったので、/data側は書き換えられ、元の/etcもそこを参照するハズです。

変更前に90-NM-〜という名前のyamlが1つある状態です。

sudo ls -l /data/etc/netplan/

-rw------- 1 root root 157 Oct  7 11:52 50-cloud-init.yaml
-rw------- 1 root root 706 Oct  7 11:55 90-NM-32f4a827-cc10-4a0e-aae9-64158b7a33e7.yaml

デスクトップでGUI操作をして、5gのWi-Fiから2gのWi-Fiに変更してみました。
結果、もう1つ増えましたね。もちろんWi-Fiに繋がっています。

-rw------- 1 root root 157 Oct  7 11:52 50-cloud-init.yaml
-rw------- 1 root root 706 Feb  9 09:54 90-NM-201e82b9-74eb-4783-a9db-48e3a5d7f14e.yaml
-rw------- 1 root root 706 Oct  7 11:55 90-NM-32f4a827-cc10-4a0e-aae9-64158b7a33e7.yaml

この状態で再起動しても、同じように2つが維持されていればOKです。確かに維持されていました。

これでOverlayfs状態でもWi-Fiの接続情報を変更することができました。

再起動でファイルが無くなればOK

最後にOverlayfsした状態で、ファイルの書き込みを検証します。
ルート直下にtest-fileを作成してみます。

sudo touch /test_file

rootfsはOverlayfsですから、メモリー内で変更されたように振る舞います。この時点では書き込めていますね。
しかし、これはtmpfsで書き込まれただけなので、再起動したら無くなります。

再起動後にファイル自体が消えて無くなれば、Overlayfsが効いているというわけです。

再起動してls -l /test_fileなどで確かめてください。
無くなっていると思います。OKですね。

Overlayfsを元に戻すには

このようにOverlayfsの状態では、再起動すると、Overlayfsにした時点に戻ります。
本来のrootfs領域は読み取り専用(ro)で、その上層にメモリー内に展開された(tmpfs)として振る舞うからです。
tmpfsは再起動をすると消えて無くなります。

rootfsの読み取り専用を通常の読み書きに戻すには、cmdline.txtに追記したoverlayroot=tmpfs:recurse=0を削除します。
そして再起動すれば元に戻ります。

再びOverlayfs化をするなら、同じようにcmdline.txtに追記すれば良いのです。
これはbootfsは書き込み可能なままだからできます。

最低限のデータを永続化

今回はOverlayfs環境で、ネットワーク関連の一部と/homeディレクトリを別パーティションに逃がしました。
ちゃんと変更が維持されていたので、データの永続化に成功しました。

シンボリックリンクでも一部のファイルなら問題無く動作します。

ただ、よくアクセスするようなファイル群を、丸ごと永続化はあまり意味がありません。特に今回は同ストレージメディア内に切っただけのパーティションだからです。
せっかくrootfsをOverlayfsで読み取り専用にしたのに、同じメディアにアクセスが増えてしまっては元も子もありません。

設定データを書き出したり、Overlayfs状態でも生成したファイルを保存する目的が望ましいでしょう。
なるべくピンポイントで設定したいですね。

補足1

実はRaspberry Pi OSだとこの手段が取れませんでした。Trixieの問題かと思ったのですが、どうやらbullseyeまでは出来ていたようで、同じ手順ではbookwormから出来なくなっています。

調べて検証した限り、シンボリックリンクもバインドマウントも期待通りの結果になりませんでした。Raspberry Pi OSの深い部分で強制的に処理されているように見受けられます。

特にバインドマウントはブロック単位でのマウントに変更されてしまい、単なるマウントに格下げされていまいます。
この辺はRaspberry Pi OSの開発者ではありませんので理由は分かりません。初心者が誤ってシステムを破壊しないようにした手段かもしれません。

Ubuntuであればテストした限りでは大丈夫でした。

補足2

Overlayfs状態でのホスト名の変更は、hostnamectlコマンドで試した限り変更は可能でしたが、SSH接続に課題が残り接続が変更前のホスト名でしか接続ができません。接続後はホスト名も変更されてはいます。SSHだけは、どうやらprettyのホスト名を拾っているようです。ホスト名の変更はOverlayfsを解除して行う方が良さそうです。

なお、Ubuntuでは初期にSSH サーバーがインストールされていません。以下、コマンドでインストールと有効化が必要です。

sudo apt install openssh-server
sudo systemctl enable ssh
sudo systemctl start ssh

記事寄稿:ラズパイダ

非エンジニアでも楽しく扱えるRaspberry Pi 情報サイト raspida.com を運営。ラズベリーパイに長年触れた経験をもとに、ラズベリーパイを知る人にも、これから始めたいと興味を持つ人にも参考になる情報・トピックを数多く発信。PiLinkのサイトへは産業用ラズベリーパイについて技術ブログ記事を寄稿。