SSD Nodes Learn 🎉 VPS $5.50/月〜
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-13

FreeBSD jailとDockerコンテナの違いを比較

FreeBSD jailは完全なFreeBSD userlandを分離し、Dockerはレジストリのレイヤー化イメージからプロセスを実行します。ソフトウェア、状態、ネットワーク、制限の違いを比較します。

FreeBSD jail と Docker コンテナの違いを 1 つの段落で説明

FreeBSD jail と Docker コンテナは、同じ問題を異なる形で解決します。どちらも 1 つの共有カーネル上で分離された userland を実行するため、仮想マシンではありません。異なるのは、内部で実行するものです。Docker コンテナは、レジストリから取得したレイヤー化イメージ内の 1 つのプロセスを実行します。jail は完全な FreeBSD userland を実行します。独自の /etc、独自の rc 起動スクリプト、独自の pkg データベースを持ち、必要な数のプロセスを実行できます。このページにあるほぼすべての違いは、この点に起因します。

SSD Nodes は FreeBSD イメージを提供していません。このプラットフォームでは FreeBSD サーバーを借りられず、以下の内容も、ここで購入できるマシン向けのインストール手順ではありません。これは 2 つの分離モデルを比較する説明です。ワークロードが実際にどちらを必要としているかを判断し、FreeBSD チームの構成を推測せずに読み取れるようにしています。

実際の jail とは

Jail は 2000 年 3 月の FreeBSD 4.0 で導入されました。cgroups より古く、Docker よりおよそ 10 年前から存在します。この仕組みは 1 回のカーネル呼び出しで動作します。jail(8) はディレクトリツリーを受け取り、jail ID を付けてその内部でプロセスを起動します。カーネルは、その ID を持つプロセスに対して、決められた操作を拒否します。jail 内のプロセスは jail 外のプロセスを確認できません。ファイルシステムを mount または unmount できません。カーネルモジュールを読み込めません。jail に割り当てられていないネットワークアドレスにも bind できません。別の namespace 種別を覚える必要はありません。機能ごとに opt-in する必要もありません。制限は 1 つの単位として適用され、jail の設定にあるパラメータで調整します。

ホスト上では、jls で実行中の jail を一覧表示できます。jexec web sh を使うと、web という名前の jail 内で shell を起動できます。

ディレクトリに FreeBSD の userland を配置すると、jail を構築できます。base system がこの処理を実行します。

sudo bsdinstall jail /usr/local/jails/containers/web

このコマンドは、使用している release の base distribution set を取得し、通常のインストール後処理を実行します。そのため、新しいサーバーの場合と同じように root password を設定し、timezone を選択します。結果として、フォルダー内に FreeBSD のインストール環境が作成されます。次に、/etc/jail.conf でその環境を定義します。

web {
  host.hostname = "web.example.internal";
  path = "/usr/local/jails/containers/web";
  ip4.addr = "10.0.0.10";
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  mount.devfs;
}

起動してから、状態を確認します。

sudo service jail start web
jls

jls の出力には、JID、hostname、IP address とともに web が表示されます。jail が表示されない場合は、sudo jail -c web を直接実行します。このコマンドは同じ設定を foreground で適用し、受け付けられなかったパラメータを表示します。そのため、サービスの出力だけでは失敗の原因が分からない状態を避けられます。

2 回確認する価値がある行は exec.start = "/bin/sh /etc/rc" です。jail を起動すると、その内部で FreeBSD の通常の boot script が実行されます。そのため、jail は自身の /etc/rc.conf で有効になっているすべてのサービスを起動します。Docker container にはこれに相当する手順がありません。image の entrypoint process を実行し、その process が停止すると終了します。

ソフトウェアの導入方法: イメージとレジストリ、または自分で構築する userland

この違いは、初日から実感できます。

Docker では、ソフトウェアを指定して受け取ります。docker pull nginx は、他の人が構築してテストした、レイヤー化されたコンテンツアドレス型のイメージを取得し、docker compose up -d はボリュームとネットワークを接続した状態で起動します。レジストリが製品の中心です。Docker のワークフローの価値の大部分は、何千ものプロジェクトが動作するイメージを公開していることにあります。そのため、VPS で Docker を実行することがプロジェクトではなく、短時間の作業になります。

FreeBSD には、jail イメージ用のデフォルトの公開レジストリがありません。空の userland を作成し、ベアサーバーをセットアップする場合と同じように、そこへインストールします。入力するコマンドは増えます。一方で、jail 内で実行されるものが、ホストと同じパッケージセットから pkg が配置したものになるため、構成をより明確に把握できます。

ツールを使えば、作業は短くなります。BastilleBSD は一般的な jail マネージャーで、パッケージとして提供されています。

sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASE

bastille setup は、ネットワーク、ストレージ、ファイアウォールを設定します。bastille bootstrap はリリースを 1 回ダウンロードし、その後に作成するすべての jail で再利用します。FreeBSD 15.1 は 2026 年 6 月にリリースされた、現在の本番用リリースです。実行するリリースに置き換えてください。

jail の作成は 1 つのコマンドで行い、内容の導入ももう 1 つのコマンドで行います。

sudo bastille create web 15.1-RELEASE 10.17.89.10/24
sudo bastille pkg web install nginx
sudo bastille service web nginx start
sudo bastille console web

bastille console web は jail 内でログインシェルを起動し、bastille list はホスト上に存在するものを表示します。ビルドを繰り返すには、Bastille のテンプレートに手順をファイルとして記述し、jail に適用します。これは、この環境における Dockerfile に最も近い仕組みです。テンプレートは各 jail で再実行されます。事前構築済みのものが届くわけではありません。

要点は簡単です。Docker は他の人が構築したものを渡します。jail では自分でインストールします。候補のソフトウェアがコンテナイメージとしてしか提供されていないなら、他の比較軸を検討する前に判断は決まります。

状態とアップグレード: ZFS が変える部分

Docker は意図的に状態を分離します。コンテナのファイルシステムは破棄可能で、データは named volume または bind mount に保存され、アップグレードは docker compose pull に続いて docker compose up -d を実行します。コンテナは置き換えられ、volume に保存していないデータはすべて失われます。このルールに従えば仕様どおりの動作ですが、忘れるとデータ損失につながります。そのため、Compose スタックでは bind mount と named volume のどちらを選ぶかが非常に重要です。

jail では状態を分離しません。これを可能にするのが ZFS です。jail 全体が1つの dataset になります。

sudo zfs snapshot zroot/jails/containers/web@pre-upgrade
sudo pkg -j web upgrade
sudo zfs rollback zroot/jails/containers/web@pre-upgrade

このコマンドを実行する前に、zfs list で実際の dataset 名を確認してください。上記のパスは handbook で使用されている構成です。snapshot の作成には約1秒かかり、jail の内容が変更されるまではほとんどディスク容量を消費しません。アップグレードによってサービスが壊れた場合、rollback により、package database や午前2時に手作業で編集した設定ファイルを含め、userland 全体を以前の状態に戻せます。Docker には組み込みの同等機能がありません。Docker のモデルは、そのような機能が必要になることを想定していないためです。

zfs clone はもう1つの要素です。snapshot の clone は、親と変更されていないブロックを共有する新しい書き込み可能な jail です。そのため、3 GB の jail の staging コピーは、変更を始めるまでディスク容量をほとんど消費しません。FreeBSD の管理者は、この方法で本番環境と同じ jail を作成し、アップグレードを事前に検証します。

base system のアップグレードは package とは別に行います。userland の独自コピーを持つ jail では、次のように実行します。

sudo freebsd-update -b /usr/local/jails/containers/web fetch install

thin jail では、この作業を繰り返す必要がありません。共有の read-only base を nullfs でマウントし、各 jail に固有の小さな書き込み可能レイヤーを与えます。そのため、base を1回 patch するだけで、すべての jail に結果が反映されます。Bastille はデフォルトで thin jail を作成します。

ネットワーク: 公開ポートとアドレス方式の選択

Docker はネットワーク構成を自動的に決定し、例外だけを公開するよう求めます。コンテナは bridge に接続され、ユーザー定義ネットワーク上ではサービス名で相互に通信し、-p 8080:80 によってそのうちの 1 つがホストに公開されます。Docker はこの動作のために独自のパケットフィルタールールを書き込みます。これが、公開されたコンテナポートが ufw をそのまま通過する仕組みでもあります。

jail では、最初に方式を選択する必要があります。方式は 2 つあります。

共有 IP。 ip4.addr = "10.0.0.10" は既存のホストインターフェースにそのアドレスを追加し、jail がそのアドレスだけを使用するよう制限します。jail は独自のネットワークスタックを持たないため、独自のファイアウォールを実行できません。また、すべてのアドレスに実際に bind することもできません。jail 内のソケットが 0.0.0.0 を要求すると、カーネルによって jail 自身のアドレスに書き換えられます。同じアドレスの port 80 で 2 つの jail が同時に listen することはできません。そのため、それぞれにアドレスを割り当てるか、前段にリバースプロキシを置きます。

VNET。 vnet; を jail に追加すると、独自のインターフェース、独自のルーティングテーブル、独自のファイアウォールルールを持つ完全なネットワークスタックが得られます。epair でホストに接続します。これは両端がそれぞれホスト側と jail 側に接続された仮想ケーブルで、ホスト側の端を bridge に接続します。これは Docker のネットワーク構成に最も近い方式で、Bastille の -V および -B jail type が使用するモードです。

ホストの port を jail に転送するには、pf redirect rule を使用します。Bastille では次のように指定します。

sudo bastille rdr web tcp 80 80

EXPOSE はなく、自動公開も行われません。アドレスまたは redirect rule によって許可されない限り、jail に到達する通信はありません。開始時の設定は増えますが、ファイアウォールはより静かになります。

リソース制限: cgroups と rctl

Docker では cgroups を使ってコンテナを制限します。制限はコンテナの定義場所に記述します。コマンドラインでは --memory=1g --cpus=1.5、Compose ファイルでは対応するキーを使用します。すでに VPS 上の Docker Compose ファイル でスタックを管理している場合、制限は対象サービスの隣に記述され、git でサービスとともに管理できます。

FreeBSD では rctl を使用します。これは有効化が必要なサブシステムです。リソースアカウンティングは、割り当てごとに少しコストがかかるため、デフォルトでは無効です。チューナブルを /boot/loader.conf に追加して再起動します。

kern.racct.enable=1

次にルールを設定し、監視します。

sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:web

rctl -hu jail:web は jail の現在の使用量を人間が読みやすい単位で表示します。そのため、問題が発生する前に、使用量が制限にどれだけ近いかを確認できます。deny アクションを指定すると、制限を超える割り当てが jail 内で失敗します。これにより、ホスト上の kill メッセージではなく、アプリケーション自身の割り当てエラーを確認できます。

rctl -a で追加したルールは、次回の再起動時に消えます。FreeBSD の rctl サービスは /etc/rctl.conf からルールを再読み込みするため、ルールをそのファイルに記述し、サービスを有効化します。

sudo sysrc rctl_enable=YES

Docker の方が明らかに便利なのは、この点です。Compose ファイルの制限は、その制限を適用するサービスと一緒にレビューできます。rctl ルールは、別のファイルに記述された行であり、別の場所で定義された jail の名前を指定します。

仮想マシンが必要な場合: bhyve

jail はホストのカーネルを共有するため、利用できないことが恒久的に残ります。別のカーネルバージョンを実行できず、カーネルモジュールを読み込めず、Linux コンテナのように Linux バイナリを実行することもできません。FreeBSD には linuxulator という Linux 互換レイヤーがありますが、実装している Linux システムコールは一部に限られます。任意の Linux イメージに対応する汎用的な解決策ではありません。

bhyve は FreeBSD のハイパーバイザーです。実際のマシン境界が必要な場合に適しています。異なるオペレーティングシステム、異なるカーネル、またはカーネルを共有したくないテナントを実行する場合です。その代わり、共有ではなく予約されるメモリが必要になり、パッチを適用するカーネルも 2 つになります。これは、Linux でコンテナと完全仮想マシンのどちらを選ぶかと同じ判断です。さらに、基盤として ネストした仮想化に対応する VPS が必要かどうかも、この判断で決まります。

エコシステム。それが多くのチームが正直なところ Docker を使う理由です

ここまでの説明はモデルに関するものでした。多くのチームがどちらを選ぶかを決めるのは、それぞれを取り巻く環境の規模です。

Docker には Docker Hub と GHCR、docker compose、1 台では足りなくなったときの Kubernetes、コンテナ対応があらかじめ組み込まれた CI runner、そしてほぼすべてのプロジェクトの README にある 1 コマンドの quickstart があります。Jail には、規模が大きく入念に保守された FreeBSD ports tree と、すぐに実行できるアプリケーションバンドルがありますが、その種類ははるかに少数です。プロジェクトが container image だけを公開している場合、FreeBSD ではドキュメントを読み、自分で各コンポーネントを組み立てる必要があります。

このトレードオフの反対側に、Jail を選ぶ理由があります。すでに ZFS を運用していて、サービス全体の snapshot と rollback を重視する場合に適しています。サービスが FreeBSD native の場合にも適しています。tenant ごとに単一のプロセスではなく完全な userland が必要な場合や、kernel、packet filter、filesystem、ドキュメントを 1 つのシステムとして一体的に保守したい場合にも適しています。FreeBSD を coherent と呼ぶときに指しているのは、最後の点です。詳しくは、Linux と FreeBSD を server platform として比較した広範な説明と、server 利用向けに FreeBSD 15 で変更された点で扱います。

最後に判断を述べます。チームがすでに Docker を理解している場合、移行コストは現実に発生するため、具体的な効果が必要です。隔離の品質を理由に切り替えないでください。2 つのモデルには十分な近さがあり、結果を左右するのは構成のほうです。サービス全体を ZFS で rollback したい場合、またはすでに FreeBSD を使っている場合に切り替えてください。

FAQ

FreeBSD で Docker イメージを実行できますか?

Linux イメージは実行できません。サポートされた方法でもありません。FreeBSD には OCI コンテナのサポートがあります。sudo pkg install -y podman-suite で Podman をインストールすると、ocijail を介してコンテナを実行できます。ocijail は、内部で実際の jail を作成するランタイムです。コンテナモニターには fdescfs/dev/fd にマウントする必要があります。また、コンテナの NAT(ネットワークアドレス変換)には pf が必要です。FreeBSD ネイティブの OCI イメージが最も適しています。Linux イメージには Linux 互換レイヤーも必要です。2026 年 8 月時点では、FreeBSD の Podman port は依然として実験的とされています。デプロイ対象が Linux イメージのスタックである場合は、Linux 上で実行してください。

FreeBSD の jail は Docker コンテナより安全ですか?

どちらも 1 つのホストカーネルを共有するため、カーネルのバグは双方にとってリスクになります。いずれも、本当に信頼できないコードに対して選ぶ境界ではありません。違いは初期状態にあります。jail は、幅広い操作を拒否した状態で開始し、パラメーターごとに再有効化します。Docker コンテナは、いくつかの capability を削除した namespace 内で root として開始し、追加の強化は明示的に有効化します。実際には、モデルよりも設定が大きく影響します。allow.mountallow.raw_sockets を有効にした jail は、慎重に設定したコンテナより安全ではありません。

jail をバックアップするにはどうすればよいですか?

データセットのスナップショットを作成して送信します。sudo zfs snapshot zroot/jails/containers/web@backup を実行し、そのスナップショットを zfs send で別の pool に送るか、別のホストへコピーするファイルに保存します。jail はユーザーランド全体を 1 つのデータセットに保持するため、スナップショットには、インストール済みパッケージとデータが同じ時点の状態で含まれます。手作業で編集したすべての設定ファイルも含まれます。これは、名前付き volume と Compose ファイルをバックアップし、残りをイメージから再構築する Docker の方法とは対照的です。

BastilleBSD は必要ですか。それとも base system だけで十分ですか?

base system だけで十分です。最初は base system から始める方が適しています。jail.confjlsjexecservice jail start でモデル全体を扱えます。これらを理解すれば、最初にそのホスト固有のツールを学ばなくても、どの FreeBSD ホストでも内容を読み取れます。Bastille はその上にある利便性レイヤーです。release の bootstrap、thin jail の作成、template の適用、pf の redirect ルールの書き込みを自動化します。まず base command を学び、jail の数が増えて入力が煩雑になったら Bastille を追加してください。