FreeBSD jailとDockerコンテナの違いを徹底比較
FreeBSD jailとDockerコンテナの構造的違いを解説します。jailはOS全体を分離し、Dockerはイメージから単一プロセスを実行します。ソフトウェア管理、ネットワーク、リソース制限の設計思想の違いを理解し、最適な分離モデルを選択するための比較資料です。
FreeBSD jail と Docker コンテナの比較
FreeBSD jail と Docker コンテナは、同一のカーネルを共有しつつユーザーランドを分離するという同じ課題を、異なるアプローチで解決する技術であり、どちらも仮想マシンではありません。両者の決定的な違いは、その内部構造にあります。Docker コンテナは、レジストリから取得したレイヤー構造のイメージに基づき、単一のプロセスを実行するように設計されています。一方、jail は FreeBSD のユーザーランド全体を内包しており、独自の /etc、rc 起動スクリプト、pkg データベースを持ち、任意の数のプロセスを稼働させることが可能です。このページで解説するその他の差異のほとんどは、この設計思想の違いに起因します。
SSD Nodes では FreeBSD イメージを提供していません。本プラットフォームで FreeBSD サーバーをレンタルすることはできず、以下の内容はここで購入可能なマシンへのインストールガイドではありません。これは、ワークロードに適した分離モデルを選択し、FreeBSD チームの構成を正しく理解するための比較資料です。
Jail とは何か
Jail は 2000 年 3 月の FreeBSD 4.0 で導入されました。これは cgroups よりも古く、Docker よりも約 10 年先行する技術です。その仕組みは単一のカーネルコールに基づいています。jail(8) はディレクトリツリーを指定し、そこに Jail ID を付与したプロセスを開始します。カーネルは、その ID を持つプロセスに対して特定の操作を拒否します。Jail 内のプロセスは、外部のプロセスを認識できず、ファイルシステムのマウントやアンマウント、カーネルモジュールのロード、割り当てられていないネットワークアドレスへのバインドができません。学習すべき名前空間の種類や機能ごとの有効化といった概念はなく、Jail の設定パラメータで調整される制限がひとまとまりとして適用されます。
ホスト側では、jls で実行中の Jail を一覧表示し、jexec web sh で web という名前の Jail 内のシェルにログインできます。
Jail を構築するには、FreeBSD のユーザーランドをディレクトリに配置します。ベースシステムにはそのための機能が備わっています。
sudo bsdinstall jail /usr/local/jails/containers/webこのコマンドは、使用しているリリースのベース配布セットを取得し、通常のインストール後処理を実行します。そのため、新しいサーバーをセットアップする際と同様に、root パスワードの設定やタイムゾーンの選択を行います。結果として、フォルダ内に 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;
}Jail を起動し、状態を確認します。
sudo service jail start web
jlsjls を実行すると、web が JID、ホスト名、IP アドレスと共に表示されるはずです。Jail が表示されない場合は、sudo jail -c web を直接実行してください。これにより、サービス出力にエラーを残すのではなく、フォアグラウンドで同じ設定を適用し、受け入れられなかったパラメータを表示できます。
特に注意すべき行は exec.start = "/bin/sh /etc/rc" です。Jail を起動すると内部で FreeBSD の通常のブートスクリプトが実行されるため、その Jail の /etc/rc.conf で有効化されているすべてのサービスが起動します。Docker コンテナにはこれに相当する手順はなく、イメージのエントリーポイントプロセスを実行し、そのプロセスが終了するとコンテナも停止します。
ソフトウェアの導入方法:イメージとレジストリ、または構築済みのユーザーランド
これは、初日に実感する違いです。
Docker では、ソフトウェア名を指定して受け取ります。docker pull nginx は、誰かが構築・テストしたレイヤー構造のコンテンツアドレス指定イメージを取得し、docker compose up -d はボリュームとネットワークを接続してそれを起動します。レジストリこそが製品です。Docker ワークフローの価値の大部分は、数千ものプロジェクトが動作するイメージを公開している点にあり、これによって VPS での Docker 運用 はプロジェクトではなく短時間の作業で済みます。
FreeBSD には、jail イメージのデフォルトの公開レジストリはありません。空のユーザーランドを作成し、そこにインストールします。これはベアメタルのサーバーをセットアップするのと同じ手順です。入力するコマンドは増えますが、透明性は高まります。jail 内で実行されるものは、ホストと同じパッケージセットから pkg が配置したものだからです。
ツールを使えば短縮できます。BastilleBSD は一般的な jail 管理ツールであり、パッケージとして提供されています。
sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASEbastille setup は、ネットワーク、ストレージ、ファイアウォールを自動的に設定します。bastille bootstrap はリリースを一度ダウンロードし、それ以降に作成するすべての 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 webbastille console web は jail 内のログインシェルを提供し、bastille list はホスト上に何が存在するかを表示します。ビルドを繰り返す場合、Bastille テンプレートが手順をファイルに保持し、それを jail に適用します。これはこの世界における Dockerfile に最も近いものです。テンプレートは各 jail で再実行されます。事前に構築されたものは何も届きません。
結論は単純です。Docker は他人が構築したものを提供し、Jail は自分でインストールしたものを提供します。検討中のソフトウェアがコンテナイメージとしてのみ提供されている場合は、他の要素を考慮する前にその時点で結論が出ます。
状態とアップグレード:ZFS が変えるもの
Docker は意図的に状態を分離します。コンテナのファイルシステムは使い捨てであり、データは名前付きボリュームまたはバインドマウントに配置されます。アップグレードは docker compose pull の後に docker compose up -d を実行する手順となります。コンテナは置き換えられ、ボリュームに保存しなかったデータはすべて消失します。このルールに従えば機能として働きますが、忘れるとデータ消失事故につながります。そのため、Compose スタックにおいて バインドマウントと名前付きボリュームの選択 が非常に重要となります。
Jail は状態を分離しません。それが機能するのは ZFS のおかげです。Jail 全体が 1 つのデータセットとなります。
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 で実際のデータセット名を確認してください。上記のパスはハンドブックで使用されているレイアウトです。スナップショットの作成には約 1 秒しかかからず、Jail の内容が変更されるまでディスク容量をほとんど消費しません。アップグレードによってサービスが破損した場合、ロールバックを実行すればパッケージデータベースや深夜 2 時に手作業で編集した設定ファイルを含め、ユーザーランド全体を以前の状態に戻せます。Docker にはこれに相当する組み込み機能はありません。Docker のモデルは、そのような機能を必要としないことを前提としているためです。
zfs clone はもう一方の重要な機能です。スナップショットのクローンは、親と変更されていないブロックを共有する新しい書き込み可能な Jail です。そのため、3 GB の Jail のステージングコピーを作成しても、変更を開始するまではディスク容量をほとんど消費しません。これが、FreeBSD 管理者がアップグレードの予行演習を行うために「本番環境と同一」の Jail を構築する方法です。
ベースシステムのアップグレードはパッケージとは別に行われます。独自のユーザーランドコピーを持つ Jail の場合は以下の通りです。
sudo freebsd-update -b /usr/local/jails/containers/web fetch installシン Jail はこの作業の重複を回避します。共有された読み取り専用のベースを nullfs 経由でマウントし、各 Jail には小さな書き込み可能レイヤーのみを持たせます。そのため、ベースを 1 回パッチ適用するだけで、すべての Jail にその結果が反映されます。Bastille はデフォルトでシン Jail を作成します。
ネットワーク:公開ポートとアドレッシングの決定
Docker はネットワーク構成を自動的に決定し、例外的な公開ポートのみを指定するよう求めます。コンテナはブリッジ上に配置され、ユーザー定義ネットワーク内ではサービス名で相互に通信します。また、-p 8080:80 を使用すると、そのうちの 1 つをホストに対して公開できます。Docker はこれを実現するために独自のパケットフィルタリングルールを書き込みますが、これが 公開されたコンテナポートが ufw を完全にバイパスする 仕組みでもあります。
Jail を使用する場合、最初にモデルを選択する必要があります。モデルは 2 つあります。
共有 IP (Shared IP)。ip4.addr = "10.0.0.10" は既存のホストインターフェースにアドレスを追加し、Jail をそのアドレスに制限します。Jail は独自のネットワークスタックを持たないため、独自のファイアウォールを実行できません。また、すべてのインターフェースで待ち受けることもできません。Jail 内のソケットが 0.0.0.0 で待ち受けようとすると、カーネルによって Jail 自身のアドレスに書き換えられます。2 つの Jail が同じアドレスの 80 番ポートを同時に待ち受けることはできないため、それぞれに別々のアドレスを割り当てるか、前段にリバースプロキシを配置する必要があります。
VNET。Jail に vnet; を追加すると、独自のインターフェース、ルーティングテーブル、ファイアウォールルールを備えた完全なネットワークスタックが提供されます。両端に接続を持つ仮想ケーブルである epair を使用してホストと接続し、ホスト側の端をブリッジに配置します。これは Docker が提供するものに最も近く、Bastille の -V および -B Jail タイプで採用されているモードです。
ホストのポートを Jail に転送するには、pf リダイレクトルールを使用します。Bastille はこれを以下のようにラップします。
sudo bastille rdr web tcp 80 80EXPOSE は存在せず、自動的な公開も行われません。Jail のアドレスまたはリダイレクトルールで許可されない限り、Jail に到達できるものはありません。これは開始までの手順は増えますが、ファイアウォールははるかに静かな状態に保たれます。
Resource limits: cgroups vs rctl
Docker limits a container with cgroups, and the limits live where the container is defined: --memory=1g --cpus=1.5 on the command line, or the matching keys in a Compose file. If you already keep your stack in a Docker Compose file on a VPS, the limit sits beside the service it applies to and travels with it in git.
FreeBSD uses rctl, and it is a subsystem you have to switch on. Resource accounting is off by default because it costs a little on every allocation. Add the tunable to /boot/loader.conf and reboot:
kern.racct.enable=1Then set a rule and watch it:
sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:webrctl -hu jail:web prints the jail's current usage in human-readable units, so you can see how close it sits to the limit before anything breaks. The deny action makes the over-limit allocation fail inside the jail, so you see the application's own allocation error rather than a kill message on the host.
Rules added with rctl -a vanish at the next reboot. FreeBSD's rctl service reloads them from /etc/rctl.conf, so write the rule into that file and enable the service:
sudo sysrc rctl_enable=YESThis is the axis where Docker is plainly more convenient. A limit in a Compose file is reviewed with the service it constrains. An rctl rule is a line in a separate file naming a jail defined somewhere else.
仮想マシンが必要な場合:bhyve
Jail はホストのカーネルを共有するため、恒久的に利用できない機能があります。異なるカーネルバージョンの実行やカーネルモジュールのロードはできず、Linux コンテナのように Linux バイナリをそのまま実行することもできません。FreeBSD には linuxulator という Linux 互換レイヤーがありますが、これは Linux システムコールの一部を実装しているに過ぎず、任意の Linux イメージを動かすための汎用的な解決策ではありません。
bhyve は FreeBSD のハイパーバイザーであり、異なるオペレーティングシステムや異なるカーネル、あるいはカーネルを共有したくないテナントなど、明確なマシン境界が必要な場合に適したツールです。その代償として、メモリは共有ではなく予約され、管理対象のカーネルがもう一つ増えることになります。これは Linux においてコンテナとフル仮想マシンのどちらを選ぶかという判断と同じであり、その判断によって ネストされた仮想化をサポートする VPS が基盤として必要かどうかが決まります。
エコシステム:多くのチームが Docker を選ぶ真の理由
前述の内容はモデルに関するものです。多くのチームがどちらを選ぶかを決めるのは、それぞれの周囲に広がる世界規模の大きさです。
Docker には Docker Hub や GHCR、docker compose、1 台のサーバーでは足りなくなった時のための Kubernetes、コンテナサポートが組み込まれた CI ランナー、そしてほぼすべてのプロジェクトの README に記載された 1 コマンドでのクイックスタートがあります。Jails には FreeBSD ports ツリーがあり、これは大規模で丁寧にメンテナンスされていますが、すぐに実行可能なアプリケーションバンドルの数は Docker に比べてはるかに少なくなります。あるプロジェクトがコンテナイメージのみを公開している場合、FreeBSD を使うにはドキュメントを読み、自分で各パーツを組み立てる必要があります。
Jails は、そのトレードオフの反対側で価値を発揮します。すでに ZFS を運用しており、サービス全体のスナップショットやロールバックを重視する場合、サービスが FreeBSD ネイティブである場合、単一プロセスではなくテナントごとに完全なユーザーランドを確保したい場合、あるいはカーネル、パケットフィルタ、ファイルシステム、ドキュメントが単一のシステムとして統合管理されていることを望む場合に、Jails が適しています。この最後の点は、FreeBSD が「一貫性がある」と評される理由であり、サーバープラットフォームとしての Linux と FreeBSD の比較や、FreeBSD 15 でのサーバー利用における変更点で詳しく解説されています。
最後に結論を述べます。チームがすでに Docker に習熟している場合、移行コストは現実的な課題であり、それに見合う具体的な成果が必要です。分離品質を理由に移行しないでください。両モデルの分離性能は同程度であり、実際には設定内容の方が重要だからです。ZFS によるサービス全体のロールバックを求めている場合、あるいはすでに FreeBSD を運用している場合に、移行を検討してください。
FAQ
FreeBSD で Docker イメージを実行できますか?
Linux イメージは実行できません。また、サポートされた手法でもありません。FreeBSD には OCI コンテナのサポートがあります。sudo pkg install -y podman-suite をインストールすると Podman が利用可能になり、ocijail を介してコンテナを実行します。これは内部で実際の jail を作成するランタイムです。コンテナモニター用に fdescfs を /dev/fd にマウントする必要があり、コンテナの NAT(ネットワークアドレス変換)には pf が必要です。FreeBSD ネイティブの OCI イメージが最も安定して動作します。Linux イメージを実行するには追加で Linux 互換レイヤーが必要ですが、2026 年 8 月現在、FreeBSD の Podman ポートは依然として実験的とされています。デプロイメントが Linux イメージのスタックである場合は、Linux 上で実行してください。RHEL 系であれば Rocky Linux または AlmaLinux への Docker のインストール が該当します。そこでは、何もインストールする前から docker コマンドを所有するパッケージとして、Podman が標準で提供されています。
FreeBSD の jail は Docker コンテナより安全ですか?
どちらもホストカーネルを共有しているため、カーネルのバグは双方にとってリスクとなります。どちらも、信頼できないコードを隔離するための境界としては不十分です。違いは出発点にあります。jail は広範な操作が拒否された状態から始まり、必要な権限をパラメータごとに有効化していきます。一方、Docker コンテナは root 権限で名前空間内に配置され、一部の機能(capabilities)を制限することで開始されるため、さらなる強化はオプトインとなります。実際にはモデルよりも設定が重要であり、allow.mount と allow.raw_sockets を有効にした jail が、慎重に設定されたコンテナよりも安全であるとは限りません。
jail をバックアップするにはどうすればよいですか?
データセットのスナップショットを作成し、送信します。sudo zfs snapshot zroot/jails/containers/web@backup を実行し、そのスナップショットを zfs send で別のプールへ転送するか、ファイルとして書き出してサーバー外へコピーします。jail はユーザーランド全体を 1 つのデータセットに保持するため、スナップショットにはインストールされたパッケージ、データ、手動で編集したすべての設定ファイルが、一貫性のある状態で記録されます。これは Docker の慣習とは対照的です。Docker では名前付きボリュームと Compose ファイルをバックアップし、残りはイメージから再構築します。
BastilleBSD は必要ですか、それともベースシステムだけで十分ですか?
ベースシステムだけで十分であり、そこから始めるのが最善です。jail.conf、jls、jexec、service jail start がモデルのすべてを網羅しており、これらを理解していれば、特定のホストツールを学ばなくてもどの FreeBSD ホストでも操作できます。Bastille はその上の利便性レイヤーであり、リリースのブートストラップ、シン jail の作成、テンプレートの適用、pf リダイレクトルールの記述を自動化します。まずはベースコマンドを習得し、jail の数が増えて入力が煩雑になった段階で Bastille を導入してください。