---
tags: [freebsd, tooling]
verified: 2026-09-02
---

# FreeBSD — Jails: контейнеризация по-BSD

## Обзор

Jails — это нативная технология изоляции FreeBSD, появившаяся в FreeBSD 4.0 (2000 год), задолго до Docker (2013) и даже Linux namespaces/cgroups. Автор — Poul-Henning Kamp, создавший jails для R&D Associates как механизм разделения сервисов в среде shared hosting.

Концептуально jail — это chroot на стероидах: полная изоляция файловой системы, процессов, сети и пользователей. Jailed-процессы не видят процессы за пределами jail, не могут модифицировать ядро, менять сетевую конфигурацию хоста или монтировать файловые системы. При этом все jail'ы разделяют одно ядро хоста — это не виртуализация, а OS-level контейнеризация.

Jails — одна из определяющих технологий FreeBSD. Они используются повсеместно: от изоляции сервисов на production-серверах до сборочных окружений poudriere, от запуска Linux-бинарников до тестирования сетевых конфигураций.

## Архитектура и механизм изоляции

### Что именно изолируется

Файловая система: jail видит только поддерево файловой системы от указанного корня (path). Процесс внутри jail не может обращаться к файлам выше этого корня — это наследие chroot, но с enforcement на уровне ядра, а не простой подменой корня.

Процессы: jail'ы изолированы друг от друга и от хоста. `ps` внутри jail показывает только процессы этого jail. PID-пространство общее (ядро одно), но видимость ограничена.

Сеть: в базовом режиме jail привязан к IP-адресу хоста. С VNET — получает полную копию сетевого стека с собственными интерфейсами, таблицей маршрутизации и firewall.

Пользователи: каждый jail имеет собственного root (UID 0), но этот root ограничен рамками jail — не может влиять на хост или другие jail'ы. Ядро внутренне помечает credentials процесса как jailed и проверяет это при каждом системном вызове.

### Чего jail НЕ делает

Jail не изолирует ядро — все jail'ы разделяют одно ядро хоста. Jail не ограничивает ресурсы по умолчанию (CPU, RAM) — для этого нужен `rctl(8)`. Jail не изолирует PID-пространство полностью — PID глобальны, но видимость ограничена. Jail не может загружать kernel modules — модули должны быть загружены на хосте.

## jail.conf: конфигурация

Начиная с FreeBSD 9.1, конфигурация jail'ов описывается в `/etc/jail.conf` (или в файлах `/etc/jail.conf.d/*.conf`). Это декларативный формат с поддержкой переменных, наследования и шаблонов.

### Минимальный пример

```conf
# Глобальные настройки (применяются ко всем jail'ам)
exec.start = "/bin/sh /etc/rc";
exec.stop  = "/bin/sh /etc/rc.shutdown";
exec.clean;
mount.devfs;

# Конкретный jail
www {
    host.hostname = "www.example.org";
    path = "/usr/local/jails/www";
    ip4.addr = "vtnet0|10.0.0.10/24";
}
```

`exec.start` и `exec.stop` — команды, выполняемые при старте и остановке jail. Для "complete" jail запускается `/etc/rc`, как при обычной загрузке FreeBSD. Для service jail — запуск конкретного демона.

`exec.clean` — запуск с чистым окружением (без переменных хоста).

`mount.devfs` — монтирование `/dev` внутри jail. Без этого большинство программ не работает. `devfs_ruleset` контролирует, какие устройства видны. Если параметр не задан, `mount.devfs` применяет набор 4 — `devfsrules_jail` из `/etc/defaults/devfs.rules`, минимальное безопасное подмножество. Набор 5 (`devfsrules_jail_vnet`) отличается тем, что дополнительно открывает `/dev/pf`, чтобы VNET-jail мог держать собственный firewall.

`path` — корень файловой системы jail.

Начиная с FreeBSD 15.0 у `jail(8)` есть параметр `zfs.dataset` — список ZFS-датасетов, которые ядро прикрепляет к jail само, при старте. Он требует `allow.mount.zfs` и избавляет от ручного `zfs jail` в хуке `exec.created`. В `jail(8)` из 14.x этого параметра нет.

### Переменные и шаблоны

jail.conf поддерживает переменные (`$name` — имя jail, `${параметр}` — подстановка), что позволяет создавать шаблоны:

```conf
# Шаблон для всех jail'ов
exec.start = "/bin/sh /etc/rc";
exec.stop  = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_${name}.log";
exec.clean;
mount.devfs;
devfs_ruleset = 4;
path = "/usr/local/jails/containers/${name}";
host.hostname = "${name}";

# Jail'ы наследуют всё из шаблона, добавляя специфичные настройки
web  { ip4.addr = "vtnet0|10.0.0.10/24"; }
db   { ip4.addr = "vtnet0|10.0.0.11/24"; }
mail { ip4.addr = "vtnet0|10.0.0.12/24"; }
```

Три строки — три jail'а. Всё общее вынесено в глобальную секцию.

## Типы jail'ов

### Thick Jails (полные)

Каждый jail содержит полную копию userland FreeBSD. Это самый простой и наиболее изолированный вариант.

Создание:

```sh
# Подготовить каталог
zfs create -o mountpoint=/usr/local/jails zroot/jails
zfs create zroot/jails/www

# Установить base system в jail
bsdinstall jail /usr/local/jails/www
# или вручную:
fetch https://download.freebsd.org/releases/amd64/amd64/15.1-RELEASE/base.txz
tar -xf base.txz -C /usr/local/jails/www

# Базовая настройка
cp /etc/resolv.conf /usr/local/jails/www/etc/
cp /etc/localtime /usr/local/jails/www/etc/
```

Trade-off: полная независимость и простота обновления каждого jail отдельно, но каждый thick jail занимает ~300-500MB на диске и обновляется индивидуально.

### Thin Jails (ZFS clone)

Базируются на ZFS-снепшоте шаблона. Создаются мгновенно, занимают почти ноль места до первых изменений (copy-on-write).

```sh
# Создать шаблон
zfs create zroot/jails/templates/15.1-RELEASE
bsdinstall jail /usr/local/jails/templates/15.1-RELEASE
freebsd-update -b /usr/local/jails/templates/15.1-RELEASE fetch install

# Снепшот
zfs snapshot zroot/jails/templates/15.1-RELEASE@base

# Клонировать для каждого jail
zfs clone zroot/jails/templates/15.1-RELEASE@base zroot/jails/containers/web
zfs clone zroot/jails/templates/15.1-RELEASE@base zroot/jails/containers/db
```

Trade-off: мгновенное создание, минимальный расход диска. Но каждый clone независим — обновление шаблона не влияет на существующие клоны. Для обновления нужно обновлять каждый jail отдельно (или пересоздавать из нового снепшота).

### Thin Jails (NullFS)

Base system монтируется read-only через nullfs из единого шаблона. Каждый jail имеет собственные writable-директории (/etc, /var, /usr/local, /tmp).

```sh
# Шаблон base (read-only)
zfs create zroot/jails/templates/15.1-RELEASE-base
# ... установить base system ...

# Skeleton — шаблон для writable-частей
zfs create zroot/jails/templates/15.1-RELEASE-skeleton
mkdir -p /usr/local/jails/templates/15.1-RELEASE-skeleton/{etc,var,tmp,usr/local,home,root}
# ... перенести etc, var, tmp и т.д. из base в skeleton ...

# Клонировать skeleton для каждого jail
zfs clone zroot/jails/templates/15.1-RELEASE-skeleton@base zroot/jails/containers/web

# fstab для jail (nullfs-монтирование)
# /usr/local/jails/web.fstab:
/usr/local/jails/templates/15.1-RELEASE-base  /usr/local/jails/web/  nullfs  ro  0  0
/usr/local/jails/containers/web               /usr/local/jails/web/skeleton  nullfs  rw  0  0
```

Trade-off: обновление base system — одна операция для всех jail'ов (`freebsd-update -b /path/to/base fetch install`, затем `service jail restart`). Наиболее экономичный подход для множества однотипных jail'ов. Но структура сложнее, отладка менее тривиальна.

### Какой тип выбрать

Для 2-5 jail'ов на VPS — thick jails на ZFS. Простота важнее экономии диска.

Для 5-20 однотипных jail'ов — thin jails (ZFS clone). Быстрое развёртывание, приемлемая сложность обновления.

Для десятков jail'ов на одном хосте — NullFS thin jails. Экономия диска и централизованное обновление перевешивают сложность настройки.

## Сеть: три модели

### IP aliasing (базовая)

Jail привязывается к IP-адресу хоста. Простейший вариант, не требующий дополнительной настройки.

```conf
www {
    ip4.addr = "vtnet0|10.0.0.10/24";
    # ...
}
```

Jail и хост разделяют сетевой стек. Jail может слушать только на своём IP. Ограничения: нет собственного localhost (127.0.0.1 принадлежит хосту), нет собственной таблицы маршрутизации, нет собственного firewall.

### ip4 = inherit

Jail наследует все IP-адреса хоста. Самый простой вариант для single-service jail'ов:

```conf
www {
    ip4 = inherit;
    # ...
}
```

### VNET (виртуальный сетевой стек)

Каждый jail получает полную копию сетевого стека: собственные интерфейсы, routing table, ARP/NDP, firewall. VNET включён в GENERIC-ядро начиная с FreeBSD 12.

Механизм: `epair(4)` — пара виртуальных Ethernet-интерфейсов, работающих как виртуальный патч-корд. Одна сторона (epairXa) остаётся на хосте и подключается к `bridge(4)`, другая (epairXb) передаётся в jail.

```conf
# /etc/rc.conf хоста
cloned_interfaces="bridge0"
ifconfig_bridge0="addm vtnet0 up"
ifconfig_vtnet0="up"
# IP назначается bridge0, не vtnet0
ifconfig_bridge0_alias0="inet 10.0.0.1/24"
```

```conf
# /etc/jail.conf
vnet_web {
    vnet;
    vnet.interface = "${epair}b";

    $id = "10";
    $ip = "10.0.0.${id}/24";
    $gateway = "10.0.0.1";
    $bridge = "bridge0";
    $epair = "epair${id}";

    exec.prestart  = "/sbin/ifconfig ${epair} create up";
    exec.prestart += "/sbin/ifconfig ${epair}a up";
    exec.prestart += "/sbin/ifconfig ${bridge} addm ${epair}a";

    exec.start  = "/sbin/ifconfig ${epair}b ${ip} up";
    exec.start += "/sbin/route add default ${gateway}";
    exec.start += "/bin/sh /etc/rc";

    exec.stop = "/bin/sh /etc/rc.shutdown";

    exec.poststop  = "/sbin/ifconfig ${bridge} deletem ${epair}a";
    exec.poststop += "/sbin/ifconfig ${epair}a destroy";

    path = "/usr/local/jails/containers/${name}";
    host.hostname = "${name}";
    allow.raw_sockets;
    exec.clean;
    mount.devfs;
    devfs_ruleset = 5;
}
```

Хуки `exec.prestart`/`exec.poststop` создают и уничтожают epair при старте/остановке jail, подключая его к bridge. Внутри jail epairXb выглядит как обычный Ethernet-интерфейс.

VNET jail может запускать собственный PF/IPFW — удобно для тестирования firewall-правил без риска заблокировать доступ к хосту. Если ошибся в правилах — просто `jexec` из хоста и исправляешь.

Для NAT из jail'ов наружу — правило на хосте:

```conf
# /etc/pf.conf хоста
nat on $ext_if from 10.0.0.0/24 to any -> ($ext_if)
```

## Управление jail'ами

### Базовые операции

```sh
# Включить jails при загрузке
sysrc jail_enable=YES

# Управление через service
service jail start www          # запустить
service jail stop www           # остановить
service jail restart www        # перезапустить
service jail start              # запустить все jail'ы

# Просмотр запущенных jail'ов
jls                             # JID, IP, hostname, path
jls -v                          # подробный вывод
jls -N                          # имя jail вместо числового JID
jls -n                          # параметры в формате name=value

# Выполнение команд внутри jail
jexec www /bin/sh               # shell в jail
jexec www pkg update            # обновить пакеты
jexec www service nginx restart # перезапустить сервис

# Вход в jail: jexec без команды запускает login-шелл указанного пользователя
jexec -u root www
```

Отдельной утилиты «войти в jail» в базе нет: `jlogin` — это подкоманда CBSD, а не команда FreeBSD.

### Управление пакетами в jail'ах

Пакеты устанавливаются внутри jail независимо от хоста:

```sh
jexec www pkg install -y nginx
jexec www sysrc nginx_enable=YES
service jail restart www
```

Каждый jail имеет собственную базу pkg в `/var/db/pkg/`. Пакеты в jail и на хосте полностью независимы.

### Обновление jail'ов

Thick jail — для запущенного jail'а удобнее флаг `-j`, он определяет версию userland самого jail'а; `-b` берёт версию из `uname -r` хоста и потому годится в основном для шаблонов, лежащих на диске:

```sh
freebsd-update -j www fetch install
service jail restart www
```

Thin jail (NullFS — обновить шаблон):

```sh
freebsd-update -b /usr/local/jails/templates/15.1-RELEASE-base fetch install
service jail restart    # перезапустить все jail'ы
```

Всё это относится к jail'ам, развёрнутым из distribution set — из `base.txz` или через `bsdinstall jail`. С FreeBSD 15.0 userland jail'а можно поставить и пакетами базовой системы (`bsdinstall` умеет цель `pkgbase`, репозиторий `FreeBSD-base` в `/etc/pkg/FreeBSD.conf` по умолчанию выключен); такой jail обновляется через `pkg(8)`, а `freebsd-update` к нему неприменим. Поддержка distribution sets и `freebsd-update` сохранится на всё время жизни ветки 15, а удалить её планируют в FreeBSD 16.

## Ограничение ресурсов: rctl

По умолчанию jail не ограничен в ресурсах. `rctl(8)` (Resource Limits) позволяет задать лимиты:

```sh
# Требуется в /boot/loader.conf:
kern.racct.enable=1

# Ограничить RAM для jail
rctl -a jail:www:memoryuse:deny=1g
rctl -a jail:www:vmemoryuse:deny=2g

# Ограничить CPU
rctl -a jail:www:pcpu:deny=50          # не более 50% одного CPU

# Ограничить число процессов
rctl -a jail:www:maxproc:deny=100

# Посмотреть текущее потребление
rctl -u jail:www

# Персистентные правила — /etc/rctl.conf
```

## Service Jails

Service jails (описаны подробнее в статье про rc-систему) — лёгкий способ изолировать отдельный rc-сервис в jail без создания отдельной файловой системы. Доступны начиная с FreeBSD 15.0; в ветку 14 их не переносили.

```sh
# /etc/rc.conf
nginx_svcj="YES"
nginx_svcj_options="net_basic"
```

Сервис автоматически запускается в jail, наследующем файловую систему хоста, но с jail-ограничениями на процессы и (опционально) сеть. Не требует создания jail rootfs, настройки jail.conf или установки пакетов в jail — используется установленный на хосте бинарник.

Это компромисс: меньше изоляции, чем у полноценного jail, но значительно проще в настройке.

## Linux Jails

FreeBSD поддерживает запуск Linux-бинарников через уровень совместимости Linuxulator. В комбинации с jail'ами это позволяет создавать jail'ы с Linux userland:

```sh
# Включить Linux-совместимость
sysrc linux_enable=YES
service linux start

# Установить Linux userland в jail: debootstrap для Debian/Ubuntu,
# порт linux_base (по умолчанию linux_base-rl9, Rocky Linux 9) для RPM-мира
# Далее — конфигурация как обычный jail, но с linux-специфичными настройками
```

Это не полноценная виртуализация Linux — ядро остаётся FreeBSD, системные вызовы транслируются. Большинство Linux-софта работает, но не всё (Docker внутри FreeBSD jail не работает, ядро-специфичный софт может не работать).

## Инструменты управления

Стандартные утилиты FreeBSD (jail, jls, jexec, jail.conf) достаточны для большинства задач. Но существуют обёртки:

`bastille` (sysutils/bastille) — наиболее активно развиваемый менеджер jail'ов. Автоматизирует создание, шаблоны, сеть, firewall. Хороший выбор для тех, кто хочет Docker-подобный workflow.

`cbsd` (sysutils/cbsd) — мощный фреймворк для управления jail'ами и bhyve VM. Более сложный, больше возможностей.

`pot` (sysutils/pot) — менеджер jail'ов с поддержкой nomad/consul.

`iocage` — исторически популярный, но мёртвый: последний релиз — 2019 год, а в апреле 2024 репозиторий переведён в архивный режим и доступен только на чтение. Для новых установок не вариант.

Рекомендация для изучения: начать со стандартного jail.conf, чтобы понять механику. Перейти на bastille, когда число jail'ов или рутинных операций станет обременительным.

## Сравнение с Docker/LXC

Jails — OS-level контейнеризация, как и LXC. Docker добавляет поверх LXC (теперь containerd) image management, registry и оркестрацию.

Jails vs Docker: jails — зрелая, стабильная технология с 26-летней историей; нет встроенной оркестрации типа docker-compose (но pot + nomad); jail'ы "тяжелее" в ощущениях, но фундаментально та же модель — общее ядро, изолированный userland.

Утверждение «у FreeBSD нет OCI-образов и своего Docker Hub» устарело. Начиная с 14.3-RELEASE проект публикует официальные OCI-совместимые образы базовой системы (`freebsd-static`, `freebsd-dynamic`, `freebsd-runtime`, `freebsd-notoolchain`, `freebsd-toolchain`) для amd64 и aarch64, в 15.0 они вошли в состав релизных артефактов, а зеркала лежат на Docker Hub и в GitHub Container Registry. Штатный инструментарий — `podman` (демонless, docker-совместимый CLI), `buildah` и `skopeo` из портов; Handbook описывает их отдельной главой «OCI Containers». Под капотом это те же jail'ы — OCI меняет способ упаковки и доставки, а не механизм изоляции.

Jails vs LXC: ближайший аналог. Обе технологии — контейнеризация на уровне ядра. FreeBSD jail появился на 8 лет раньше. VNET jails дают полную изоляцию сетевого стека, аналогичную network namespaces в Linux.

Ключевое преимущество jail'ов: они глубоко интегрированы в систему. ZFS snapshots для мгновенного клонирования, rc-система для управления жизненным циклом, pf для сетевой фильтрации, rctl для ресурсных лимитов — всё это нативные компоненты FreeBSD, работающие вместе без дополнительных слоёв абстракции.

## Практический рецепт: nginx в VNET jail на ZFS

```sh
# 1. Подготовка хоста
sysrc jail_enable=YES
sysrc cloned_interfaces+="bridge0"
sysrc ifconfig_bridge0="addm vtnet0 inet 10.0.0.1/24 up"
sysrc ifconfig_vtnet0="up"
sysrc gateway_enable=YES

# 2. Создание jail
zfs create -o mountpoint=/usr/local/jails zroot/jails
zfs create zroot/jails/web
bsdinstall jail /usr/local/jails/web
cp /etc/resolv.conf /usr/local/jails/web/etc/
cp /etc/localtime /usr/local/jails/web/etc/

# 3. Настройка rc.conf внутри jail
cat > /usr/local/jails/web/etc/rc.conf << 'EOF'
ifconfig_epair10b="inet 10.0.0.10/24"
defaultrouter="10.0.0.1"
nginx_enable="YES"
EOF

# 4. Установка nginx в jail
pkg -j web install -y nginx    # или после первого запуска: jexec web pkg install nginx

# 5. Конфигурация jail.conf — использовать VNET-шаблон из секции выше

# 6. NAT на хосте
# В /etc/pf.conf:
# nat on vtnet0 from 10.0.0.0/24 to any -> (vtnet0)
# pass all
```

## Источники

- FreeBSD Handbook, Chapter 17 "Jails and Containers": [docs.freebsd.org/en/books/handbook/jails/](https://docs.freebsd.org/en/books/handbook/jails/)
- FreeBSD Handbook, Chapter 18 "OCI Containers": [docs.freebsd.org/en/books/handbook/containers/](https://docs.freebsd.org/en/books/handbook/containers/)
- FreeBSD Architecture Handbook, "The Jail Subsystem": [docs.freebsd.org/en/books/arch-handbook/jail/](https://docs.freebsd.org/en/books/arch-handbook/jail/)
- "Jail vnet by Examples", Olivier Cochard-Labbé (FreeBSD Foundation): [freebsdfoundation.org/wp-content/uploads/2020/03/Jail-vnet-by-Examples.pdf](https://freebsdfoundation.org/wp-content/uploads/2020/03/Jail-vnet-by-Examples.pdf)
- "Virtualize Your Network on FreeBSD with VNET": [forums.freebsd.org/threads/virtualize-your-network-on-freebsd-with-vnet.78155/](https://forums.freebsd.org/threads/virtualize-your-network-on-freebsd-with-vnet.78155/)
- `jail(8)`: [man.freebsd.org/cgi/man.cgi?query=jail&sektion=8](https://man.freebsd.org/cgi/man.cgi?query=jail&sektion=8)
- `jail.conf(5)`: [man.freebsd.org/cgi/man.cgi?query=jail.conf&sektion=5](https://man.freebsd.org/cgi/man.cgi?query=jail.conf&sektion=5)
- `rctl(8)`: [man.freebsd.org/cgi/man.cgi?query=rctl&sektion=8](https://man.freebsd.org/cgi/man.cgi?query=rctl&sektion=8)
- `epair(4)`: [man.freebsd.org/cgi/man.cgi?query=epair&sektion=4](https://man.freebsd.org/cgi/man.cgi?query=epair&sektion=4)
- `vnet(9)`: [man.freebsd.org/cgi/man.cgi?query=vnet&sektion=9](https://man.freebsd.org/cgi/man.cgi?query=vnet&sektion=9)

---

## Связанные заметки

- [FreeBSD — сеть](./freebsd-networking.md) — VNET, bridge/epair и NAT для jail-сетей
- [FreeBSD — ZFS](./freebsd-zfs.md) — датасеты как основа для jail-хранилища и снепшотов
- [FreeBSD VPS: Honeypot](./freebsd-honeypot-guide.md) — практический сценарий изоляции сервиса в jail
- [Linux — контейнеризация](./linux-containerization.md) — namespaces и cgroups, для сравнения с jail
- [FreeBSD — сводный cheatsheet](./freebsd-cheatsheet.md) — быстрая справка по jail-командам
- [FreeBSD — bhyve: виртуализация после jails](./freebsd-bhyve.md) — следующий уровень изоляции: своё ядро вместо общего, когда jail принципиально не подходит
- [Структура ядра: монолит, микроядро и что между](./os-kernel-structure.md) — общее ядро как модель: что даёт вызов функции вместо сообщения и чем за это платят
