SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-26

ZFSのRAM消費は?FreeBSDとLinuxをVPSで比較

ZFSのチェックサム、スナップショット、send/receive、圧縮とARCのRAM消費を解説します。2 GBや4 GBのVPSでメモリをどう判断するか分かります。

ZFS が提供するものと、必要になるもの

FreeBSD と Linux の ZFS は現在、OpenZFS という 1 つのコードベースに統合されているため、両システムで機能は同じです。ZFS を実行するサーバーでは、チェックサムによるデータ検証、データが変更されるまで容量を消費しないスナップショット、zfs send によるレプリケーション、プロパティ 1 つで有効にできる圧縮を利用できます。一方で、メモリが必要です。ARC(adaptive replacement cache)はデフォルトで RAM の大きな割合を使用します。2 GB または 4 GB の VPS(virtual private server)では、そのメモリはアプリケーションが必要とするメモリと重なります。

このガイドでは、40 台のドライブベイを備えたストレージ装置ではなく、仮想ディスクを 1 台または 2 台備えたレンタル VPS を前提に ZFS を評価します。その環境でも有効な機能こそ、検討する価値があります。環境を移すと効果を発揮しない部分については、プールを構築する前に把握しておく必要があります。

FreeBSD と Linux の OpenZFS:1 つのコードベース、2 つのパッケージング事情

FreeBSD では、2008 年の FreeBSD 7.0 以降、ZFS がベースシステムに含まれています。当初は実験的な機能でした。2020 年 12 月の OpenZFS 2.0 以降、FreeBSD と Linux は同じソースツリーからビルドされるため、zfs と zpool は両方で同じように動作し、一方で作成したプールをもう一方にインポートできます。

Linux では ZFS がパッケージとして提供され、FreeBSD ではベースシステムの一部になっている理由は、ライセンスにあります。OpenZFS は CDDL(Common Development and Distribution License)の下で提供されています。Linux カーネルは GPL(General Public License)version 2 の下で提供されています。カーネルプロジェクトではこの 2 つを互換性のないライセンスとして扱っているため、ZFS のコードは mainline Linux に統合されていません。そのため、各ディストリビューションが提供方法を決めています。FreeBSD にはこのような衝突がないため、ZFS はそのまま利用できます。実際に重要なのは、パッケージングに違いが 1 つあることだけです。どちらかを選ぶ必要はありません。

SSD Nodes では FreeBSD イメージを提供していないため、ここで借りるサーバーでは、このガイドの Linux 向けの内容を使用します。別の環境で FreeBSD を実行する場合は、FreeBSD サーバーで、モジュールをビルドしたり、カーネルのアップグレードに対応したりすることなく ZFS を利用できます。

ZFS のインストールとプールの作成

Ubuntu ではモジュールが kernel packages に含まれているため、コマンドだけをインストールします。

sudo apt update
sudo apt install -y zfsutils-linux
zfs version

zfs version は userland version と kernel module version の2行を出力します。1行しか表示されない場合、モジュールはロードされていません。パッケージは universe component に含まれています。Ubuntu の server image ではデフォルトで有効になっています。apt で見つからない場合は、先に sudo add-apt-repository universe を実行します。

Debian ではパッケージが contrib component に含まれ、モジュールは DKMS(dynamic kernel module support)によってマシン上でビルドされます。contrib を /etc/apt/sources.list.d/debian.sources の Components: 行に追加し、sudo apt update を実行してから、次のコマンドを実行します。

sudo apt install -y linux-headers-$(dpkg --print-architecture) zfs-dkms zfsutils-linux

インストール時にモジュールがコンパイルされ、数分かかる Building initial module for 6.12.0-... が出力されます。これは、kernel upgrade のたびにモジュールが再ビルドされるという意味です。ビルドに失敗すると、修正するまでプールを import できません。

FreeBSD では何もインストールされていません。サービスを有効化して起動します。

sysrc zfs_enable=YES
service zfs start

次にプールを作成します。まず stable device paths を確認してください。/dev/vdb は検出順に割り当てられるため、別の volume を接続すると変わることがあります。

ls -l /dev/disk/by-id/
sudo zpool create -o ashift=12 tank /dev/disk/by-id/virtio-abc123def456
zpool status tank

zpool status は、デバイスが tank の下に表示された状態で state: ONLINE を出力するはずです。ashift=12 はプールの最小 block size を 4 KiB に固定します。この値は現在の SSD に適しており、作成後は変更できません。

多くの rented image は ext4 root から起動するため、ここでの ZFS は root filesystem ではなく、2つ目の volume 上の data pool として使用します。作成する前に、そのデバイスが想定したものか確認してください。購入した NVMe disk であることの確認には1分しかかかりませんが、再構築には午後いっぱいかかることがあります。

チェックサムによる修復にはプールの冗長性が必要です

ZFS が書き込むすべてのブロックにはチェックサムが付与され、読み出し時に必ず検証されます。検出は常に機能します。修復には別のコピーが必要です。

単一ディスクのプールでは、ZFS は事実を報告して処理を終了します。zpool status -v では次のように表示されます。

status: One or more devices has experienced an error resulting in data
        corruption.
action: Restore the file in question if possible.  Otherwise restore the
        entire pool from backup.
errors: Permanent errors have been detected in the following files:

        /tank/data/archive.tar

問題のあるファイル名が示されます。ext4 なら、そのバイト列を何も報告せずに返していたため、これだけでも有用です。ただし、プール内に修復元となる別のコピーがないため、ZFS でも修復できません。

ミラー構成では、同じ読み出しが正常な側から提供され、問題のあるブロックが書き直されます。そのイベントは zpool status の CKSUM 列に表示されます。これが自己修復であり、2 台のデバイスが必要です。

sudo zpool create -o ashift=12 tank mirror /dev/disk/by-id/DISK1 /dev/disk/by-id/DISK2

VPS では、ホスト側のストレージは通常すでに冗長化されており、ハイパーバイザー上の RAID 10 がよく使われます。これにより、ドライブの故障からは保護されます。しかし、どのコピーが正しいかをアレイ側で判断できないため、ブロックが誤った内容で返されたことまでは検出できません。ZFS は、自身が書き込んだチェックサムとデータを比較するため、検出できます。

仮想ディスクが 1 台で、ある程度の修復機能が必要な場合は、sudo zfs set copies=2 tank/important により、そのデータセットの各ブロックを同じディスク上に 2 つ保存できます。データセットが使用する容量は 2 倍になります。これにより不良ブロックには対応できますが、ボリューム全体が消失した場合には何もできません。

スクラブはプール内のすべてを読み出し、検証します。

sudo zpool scrub tank
zpool status tank

正常なプールでは、scan: scrub repaired 0B in 00:04:11 with 0 errors のような行で終了します。スケジュールに登録してください。小規模なプールなら月 1 回で十分です。

systemctl list-unit-files 'zfs-scrub*'
sudo systemctl enable --now zfs-scrub-monthly@tank.timer

データセットをポリシーの単位にする

データセットはプール内のファイルシステムです。作成コストも低いため、ジョブごとに 1 つ作成します。プロパティはプールから下位へ継承されるため、デフォルトを 1 回設定し、必要な場所だけ上書きできます。FreeBSD では通常、この方法で jail を運用します。jail ごとに 1 つのデータセットを割り当てるため、個別にスナップショットを作成してロールバックできます。これは、jail と Docker コンテナを分ける要素の 1 つです。

sudo zfs create tank/data
sudo zfs create tank/pg
sudo zfs set compression=lz4 tank
sudo zfs set atime=off tank
sudo zfs set quota=20G tank/data
sudo zfs set recordsize=16K tank/pg
zfs get -r compression,compressratio,quota tank

圧縮は、慎重になって無効にされやすいプロパティですが、その判断は逆です。lz4は CPU を少量消費し、ディスクへ書き込むバイト数を減らすため、圧縮可能なデータでは通常、読み書きが速くなります。zstdはより多くの CPU を使って強く圧縮するため、頻繁には読み戻さないログやアーカイブに適しています。実際に得られている結果は zfs get compressratio tank で確認してください。圧縮率の対象になるのは、プロパティ設定後に書き込まれたデータだけです。

recordsizeは、データセットが書き込むブロックの最大サイズです。デフォルトは 128K です。データベースが 128 KiB のレコードに 8 KiB のページを書き込むと、小さな書き込みがレコード全体の読み取り、変更、書き戻しに変わります。データをロードする前に、データベースのデータセットで recordsize=16Kを設定してください。このプロパティは新しく書き込まれるブロックにだけ適用されます。

quotaは、1 つのデータセットがプールを満杯にするのを防ぐための設定です。使用率が 100% 近くになった ZFS プールは遅くなり、整理も難しくなります。そのため、意図的に空き容量を残してください。

データが変更されるまで、スナップショットのコストは発生しません

ZFS は使用中のブロックを上書きしません。新しいブロックを書き込み、ポインターを更新します。これが copy-on-write の仕組みです。スナップショットは「このデータセットが現在指しているブロックを保持する」という記録です。そのため、作成は瞬時に完了し、容量も消費しません。

sudo zfs snapshot tank/data@2026-08-11
zfs list -t snapshot -o name,used,refer -r tank/data

スナップショットの USED 列は、そのスナップショットだけが保持している容量を示します。最初はほぼ 0 ですが、データを変更または削除すると増加します。古いブロックを解放できなくなるためです。

ファイルの復元に、復元処理は必要ありません。

ls /tank/data/.zfs/snapshot/
cp /tank/data/.zfs/snapshot/2026-08-11/notes.txt /tank/data/notes.txt

.zfs ディレクトリは、sudo zfs set snapdir=visible tank/data を実行するまで ls -a からも隠されています。必要になる前にスナップショットを作成してください。スナップショットがない状態で誤って rm -rf を実行すると、ext4 の復旧手順に移行します。この手順では最初にディスクをアンマウントし、その後の対応はさらに複雑になります。

ロールバックを実行すると、スナップショット作成後に書き込まれたすべての内容が破棄されます。

sudo zfs rollback tank/data@2026-08-11

新しいスナップショットが存在すると処理は拒否されます。-r を使うと、それらの新しいスナップショットを破棄して処理を続行できます。Enter キーを押す前に、データセット名を 2 回確認してください。

スナップショットはバックアップではありません。同じプール、同じボリューム、同じサーバー上に存在します。ボリュームの障害や、zpool destroy が発生すると、データとともにスナップショットも失われます。スナップショットは、自分自身の rm やアップグレードの失敗から保護します。これは実際の障害の多くをカバーしますが、プール自体に起きた問題からは何も保護しません。詳しくは、VPS のスナップショットがバックアップではない理由を参照してください。

送受信: 1 つのコマンドでレプリケーション

zfs send はスナップショットを標準出力のバイトストリームに変換し、zfs receive はそのストリームをデータセットに戻します。最初のコピーはフル送信です。

sudo zfs snapshot tank/data@daily-2026-08-11
sudo zfs send tank/data@daily-2026-08-11 | ssh backup.example.com "sudo zfs recv -F backup/data"

その後は、2 つのスナップショット間で変更された内容だけを送信します。

sudo zfs snapshot tank/data@daily-2026-08-12
sudo zfs send -i tank/data@daily-2026-08-11 tank/data@daily-2026-08-12 | ssh backup.example.com "sudo zfs recv backup/data"

受信側には、送信元のスナップショットが存在していなければなりません。存在しない場合、差分を適用する基準が ZFS にないため、受信は cannot receive incremental stream: most recent snapshot of backup/data does not match incremental source で停止します。両側にあるスナップショットから送信するか、フル送信でやり直してください。

リモートの root を使う代わりに、送信先で権限を付与します: sudo zfs allow -u backupuser create,mount,receive backup/data。

これは、1 つの条件を満たせば実際のオフサイトバックアップになります。遠隔側は ZFS pool でなければなりません。object storage はストリームを受信できないためです。送信先が S3-compatible storage または通常の Linux host の場合は、その環境に対応したツールを使用してください。VPS からの restic バックアップで、その方法を説明しています。

ZFS はなぜこれほど多くの RAM を使用するのか? ARC

ARC(adaptive replacement cache)は、ZFS の読み取りキャッシュです。通常の Linux のページキャッシュではなくカーネルメモリ上にあるため、free -h には buff/cache として表示されません。使用中のメモリとして表示されます。使用率がほぼ上限に見える ZFS サーバーの多くは、キャッシュが温まっているだけです。これは「ZFS が RAM を使い果たした」という報告の大半を説明します。

デフォルトの上限が大きいのは意図的な設計です。OpenZFS 2.3 では、ARC の最大サイズは RAM から 1 GiB を引いた値と、RAM の 5/8 のうち大きい方です。OpenZFS 2.2 以前の Linux では RAM の半分が使用されていました。一方、FreeBSD ではすでに新しいルールが使われていました。自分の環境にどのルールが適用されるかは、zfs version を実行して確認してください。

ChartDefault maximum ARC size by instance RAM, from the OpenZFS default rule (GiB)
The data behind this chart
[
  {
    "label": "2 GB VPS",
    "openzfs_2_2_linux_gib": 1,
    "openzfs_2_3_gib": 1.25
  },
  {
    "label": "4 GB VPS",
    "openzfs_2_2_linux_gib": 2,
    "openzfs_2_3_gib": 3
  },
  {
    "label": "8 GB VPS",
    "openzfs_2_2_linux_gib": 4,
    "openzfs_2_3_gib": 7
  },
  {
    "label": "16 GB VPS",
    "openzfs_2_2_linux_gib": 8,
    "openzfs_2_3_gib": 15
  }
]

この数値は、一般的なインスタンスサイズに文書化されたデフォルトルールを適用した値です。稼働中のサーバーで測定した値ではありません。4 GB のインスタンスでは、2.3 のルールにより ARC は 3 GiB まで使用できます。同じサーバーを 2.2 で動かすと、2 GiB で上限に達します。2.3 のルールでは、2 GB のインスタンスでも 1.25 GiB まで使用できます。アプリケーションが使用できるのは、残ったメモリです。

表をそのまま信頼せず、実際のサーバーで数値を確認してください。

grep -E '^(size|c_max) ' /proc/spl/kstat/zfs/arcstats
arc_summary | head -n 20

3 列目はバイト単位です。c_max は現在適用されている上限で、size は ARC が現在保持しているサイズです。

ARC はメモリを返します。カーネルがメモリープレッシャーを通知すると、ARC は縮小します。問題はタイミングです。縮小はメモリープレッシャーによって開始されるため、プロセスが一度に数百 MiB を要求すると、ARC が解放処理を続けている間に OOM(out of memory)killer によって終了することがあります。データベースと Web サーバーを 2 GB のサーバーで動かしている場合、これは珍しい事象ではありません。OpenZFS のマニュアルにも、手動で上限を変更する場合について同じ説明があります。上限を下げても、「縮小を促すメモリープレッシャーがなければ ARC は縮小しない」とされています。

小規模 VPS で ARC に上限を設定する方法

まず、ワークロードに必要なメモリを見積もります。データベースとアプリケーションに必要な容量を合計し、オペレーティングシステム用の余裕を残して、残りを ARC に割り当てます。Postgres と 1 つの Web アプリケーションを 4 GB のインスタンスで実行する場合、ARC は 512 MiB から 1 GiB が妥当な開始値です。

設定を即時に反映します。ここでは 1 GiB に設定します。

echo 1073741824 | sudo tee /sys/module/zfs/parameters/zfs_arc_max

再起動後も設定が維持されるようにします。

echo 'options zfs zfs_arc_max=1073741824' | sudo tee /etc/modprobe.d/zfs.conf
sudo update-initramfs -u

initramfs の手順が重要なのは、root ファイルシステムがマウントされる前に initramfs からモジュールがロードされる可能性があるためです。その場合、先ほど書き込んだファイルは読み込まれません。再起動後、arcstats の c_max 行で設定を確認します。

注意点が 2 つあります。マニュアルにもあるとおり、システムの稼働中に値を 0 へ戻すことはできません。元に戻すにはファイルを編集して再起動します。また、値を下げても、大きな ARC がその場で縮小するわけではありません。

FreeBSD では、同じ上限を vfs.zfs.arc 配下の sysctl で設定します。sysctl vfs.zfs.arc を実行して現在の値と、使用中のバージョンで使われている正確な名前を確認し、最大値を /boot/loader.conf に書き込みます。

小規模サーバーでは、メモリに関してさらに 2 つのルールがあります。重複排除は無効にしてください。重複排除テーブルはメモリ上に保持され、一般的な目安では一意なデータ 1 TB あたり 1 から 3 GB の RAM が必要です。また、swap を zvol(プールから切り出したブロックデバイス)に配置しないでください。メモリを解放しようとしているファイルシステムを経由して swap を行うと、マシンがデッドロックする可能性があります。swap は通常のパーティション、またはプールの外部にある swap file に配置してください。

ext4 または XFS と restic のほうが適している場合

ZFS は、余裕のあるメモリと 2 台目のボリュームがあるサーバーで効果を発揮します。それ以外の環境では、通常のファイルシステムと実績のあるバックアップツールを組み合わせるほうが適しています。次の場合は ext4 または XFS を選択します。

  • インスタンスの RAM が 2 GB または 4 GB で、ワークロードがその全容量を必要とする。
  • 仮想ディスクが 1 台しかなく、2 つ目のコピーもないため、ZFS を使っても検出しかできず、修復できない。
  • バックアップ先がオブジェクトストレージまたは通常の Linux ホストで、そこでは zfs send ストリームを受け取れない。
  • DKMS を使用する Debian を運用しており、モジュールがビルドされない状態でカーネルをアップグレードする余裕がない。
  • root ファイルシステムで ZFS が必要だが、プロバイダーのイメージが ext4 しか提供していない。

別のデータボリュームがあり、十分な RAM(8 GB 以上が快適)を確保でき、スナップショットと zfs send を有効にするだけでなく、それらを実際に使用する計画があるなら、ZFS を使い続けます。それ以外の場合は、ext4 と restic を組み合わせ、サーバーが管理しないストレージへ暗号化・重複排除済みのバックアップを書き出せば、メモリを消費せずに同じ目的の多くを達成できます。

障害の種類と表示される文字列

再起動後にプールが消えている。 zpool status は no pools available を表示します。import サービスは /etc/zfs/zpool.cache を読み込むため、このファイルにないプールはブート時に import されません。sudo zpool import で import 可能なプールを一覧表示し、sudo zpool import tank で復旧し、sudo zpool set cachefile=/etc/zfs/zpool.cache tank で永続化します。別のシステムから正常に export されていないプールでは cannot import 'tank': pool may be in use from other system が表示されます。ほかのホストがそのプールを使用していないことを確認したら、sudo zpool import -f tank でこの状態を上書きできます。

Debian でカーネル更新後に modprobe: FATAL: Module zfs not found in directory /lib/modules/6.12.0-... が発生する。 DKMS が新しいカーネル用にビルドされていません。通常は、対応する headers がインストールされていないことが原因です。dkms status で、どのカーネル用に何がビルドされているかを確認できます。次に sudo apt install -y linux-headers-$(uname -r) を実行してから sudo dkms autoinstall で再ビルドし、sudo zpool import tank でプールを復旧します。

ファイルを削除したのにプールが満杯である。 snapshot が参照している削除済みデータはディスク上に残るため、du と df の結果が一致しません。zfs list -o space -r tank で使用量を USEDDS と USEDSNAP に分けて確認します。USEDSNAP が大きければ、それが原因です。sudo zfs destroy tank/data@2026-06-01 で古い snapshot を破棄すると、空き容量が戻ります。

zpool status の CKSUM カウントが増え続ける。 ZFS より下位の何かが不正なデータを返しています。mirror では警告を示し、ブロックは修復済みです。単一ディスクのプールではファイルが失われています。zpool status -v でそのファイルを特定し、このプール上にないバックアップからそのファイルだけを復元します。

サーバーが遅く、swap が発生している。 前述の方法で ARC の上限を設定し、arc_summary を実行して hit ratio を確認します。作業セットを保持できないほど ARC が小さいと、すべての読み取りがディスクへ移ります。この状態では、page cache を使用する通常の filesystem のほうが適しています。

FAQ

ZFS には VPS 上でどの程度の RAM が必要ですか?

ZFS は 2 GB のインスタンスで動作します。重要なのは、アプリケーションにどれだけ RAM が残るかです。チューニングしない場合、OpenZFS 2.3 では ARC が RAM から 1 GiB を引いた値と RAM の 5/8 のうち大きい方まで拡張できます。そのため、4 GB のサーバーでは 3 GiB をキャッシュに割り当てられます。zfs_arc_max をワークロードで使用できる値に設定し、/proc/spl/kstat/zfs/arcstats から c_max の行を読み取って確認します。

ZFS の snapshot はバックアップですか?

いいえ。snapshot はデータと同じ pool に保存されます。これは不正な rm やアップグレードの失敗からは保護できますが、pool やインスタンスが失われると同時に失われます。zfs send で別のマシンへ送信するか、このサーバーが管理していないストレージに書き込むバックアップツールを実行して、バックアップに変換します。

ZFS は FreeBSD と Linux で同じように動作しますか?

2020 年 12 月の OpenZFS 2.0 以降、同じコードベース、同じコマンド、同じ on-disk format を使用しており、pool は両者の間で移行できます。違いはパッケージングです。FreeBSD では、ZFS は base system に含まれています。Linux では、各ディストリビューションが方法を決めます。Ubuntu は module を kernel package に組み込みます。一方、Debian は DKMS を使用してマシン上で module をビルドするため、再ビルドが成功するまで kernel upgrade 後に module がなくなることがあります。

1 台のディスクを持つ VPS で、ZFS は破損を修復できますか?

破損を検出してファイル名を示せますが、修復はできません。修復には block の 2 つ目のコピーが必要だからです。dataset で zfs set copies=2 を有効にすると、容量を 2 倍使ってその 2 つ目のコピーを作成できます。これは不良 block には対処できますが、volume の消失には対処できません。実際に修復できる構成は、2 つの volume にまたがる mirror です。

圧縮によってサーバーは遅くなりますか?

lz4 によって、通常は高速になります。圧縮された block では、書き込む byte 数と読み取る byte 数が少なくなります。また、block ごとの CPU コストは、節約できる disk の処理量と比べて小さいものです。pool root で compression=lz4 を設定すると、すべての dataset がその設定を継承します。その後、実データを書き込んだら zfs get compressratio tank を確認します。