Іноді на FreeBSD треба запустити якісь сервіси, які офіційно FreeBSD не підтримують, і власне, цей пост з’явився через те, що я встановлював Open WebUI на своєму NAS – і як раз Open WebUI простіше було зробити на Linux.
Тому підняв його у FreeBSD Linux jail, а для створення контейнеру взяв Bastille, яка спрощує менеджмент.
Про сам Open WebUI може допишу чорнетку, а Bastille вирішив винести окремим постом – бо зараз буду сетапити Hermes Agent (вже – див. Hermes Agent: запуск AI Agent у FreeBSD Jail з Bastille, і хочеться мати таку собі коротку інструкцію по тому, як працювати з FreeBSD jails використовуючи Bastille.
Про сам мій NAS є ціла серія постів, вже 15 штук, див. початок у FreeBSD: Home NAS, part 1 – налаштування ZFS mirror.
Зміст
What is the FreeBSD Jails та Bastille?
Щоб відповісти на це питання – треба спочатку згадати що таке FreeBSD Jails.
FreeBSD jails – аналог Docker/ContainerD в Linux – але з’явились набагато раніше, ніж Linux LXC та namespaces та cgroups, які потім “еволюціонували” в Docker. Про Linux cgroups писав детально у Kubernetes: Pod resources.requests, resources.limits та Linux cgroups.
FreeBSD jails з’явились ще у 1999 році як розвиток “неповноцінної” системи chroot, яка не давала повної ізоляції. З jails з’явилась можливість відокремлення filesystem, мати окремий network stack, власні PIDs і так далі – власне все те, до чого ми звикли в Linux та його контейнерах.
Як раз на днях зустрів цікавий пост на цю тему, де серед іншого говориться і за історію контейнеризації – Your Container Is Not a Sandbox.
На відміну від контейнерів в Linux – FreeBSD jails це єдина частина ядра системи, тоді як Linux – це комбінація різних механізмів (namespaces, cgroups).
Правда, вона має і недоліки – бо це все одно залишається одне і там саме ядро FreeBSD, зато вона простіша – а тому безпечніша і простіша в роботі та дебагу.
Власне, Bastille – це розвиток системи jails, точніше – система для спрощення менеджменту контейнерів у FreeBSD, аби не писати jail.conf руками і мати простий CLI для управління (як Docker – це “обгортка” для Linux containers).
Не Bastille єдиною – є аналогічні рішення як-от iocage, ezjail, pot та інші.
Чому взяв Bastille – проект активно розвивається, є велике комьюніті, має зручний CLI та добре інтегрується з можливостями ZFS.
Установка Bastille
Встановлюємо з репозиторію, додаємо в автостарт:
[root@test-free-15-bastille ~]# pkg install bastille [root@test-free-15-bastille ~]# sysrc bastille_enable=YES bastille_enable: -> YES
Перевіряємо ім’я ZFS Pool:
[root@test-free-15-bastille ~]# zpool list -Ho name zroot
Див. ZFS Support.
Додаємо підтримку ZFS до Bastille – файл /usr/local/etc/bastille/bastille.conf:
... ## ZFS options bastille_zfs_enable="YES" bastille_zfs_zpool="zroot" bastille_zfs_prefix="bastille" ...
Bastille setup: базові налаштування системи
Перевіряємо версію хоста:
[root@test-free-15-bastille ~]# freebsd-version 15.0-RELEASE
Можна виконати bastille setup – система сама налаштує мережу, про мережу трохи детальніше далі:
[root@test-free-15-bastille ~]# bastille setup bastille_enable: YES -> YES ZFS has already been configured! Configuring bastille0 loopback interface cloned_interfaces: -> lo1 ifconfig_lo1_name: -> bastille0 Bringing up new interface: [bastille0] Created clone interfaces: lo1. bastille_network_loopback: bastille0 -> bastille0 bastille_network_shared: -> Loopback interface successfully configured: [bastille0] Determined default network interface: (em0) /usr/local/etc/bastille/pf.conf does not exist: creating... pf_enable: NO -> YES Bastille pf ruleset created. Please review '/usr/local/etc/bastille/pf.conf' and enable pf using 'service pf start'. Bastille has successfully been configured.
Відразу включило Packet Filter – треба для NAT, і створило правила. Див. FreeBSD: Home NAS, part 2 – знайомство з Packet Filter (PF) firewall.
Якщо PF не запущений – запускаємо (якщо по SSH – підключення буде розірване):
[root@test-free-15-bastille ~]# service pf start
І після setup вже маємо новий loopback інтерфейс:
[root@test-free-15-bastille ~]# ifconfig bastille0
bastille0: flags=8008<LOOPBACK,MULTICAST> metric 0 mtu 16384
options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
groups: lo
nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
Мережа для jails
Документація – Networking та класний пост Managing Jails in FreeBSD with Bastille (2022 рік, але в цілому актуальний).
Bastille підтримує кілька тип нетворку:
- VNET (DHCP): Bastille створює інтерфейс з типом bridge і підключає jail через
epair– кожен jail отримує власні MAC та IP адреси, і виглядає як окремий хост у мережі - Bridged VNET (own bridge): те саме, але bridge створюється вручну – використовується для кастомних або ізольованих мереж
- Alias/Shared Interface: один інтерфейс хоста, IP-адреси для jail-ів додаються як alias фізичного інтерфейсу хоста – простий варіант, але без окремого network stack (тобто у всіх буде загальний фаервол самого хоста, роутинг тощо)
- NAT/Loopback Interface: jail отримує IP у внутрішній мережі і ходить в “світ” через NAT хоста, для доступу ззовні до jail потрібен port forwarding
- Inherit: jail використовує той самий IP і інтерфейс, що й хост, використовується рідко – зазвичай для специфічних кейсів, доступ розділяється по портах – незручно, не гнучко, не масштабується
Далі детальніше подивимось на три основних типи – VNET, Alias та NAT.
Bastille bootstrap
Запускаємо bastille bootstrap, аналог docker pull – скачати базовий архів з системою, яку передамо аргументом, та розархівувати його для подальшого використання.
Якщо робимо контейнер з FreeBSD, то версія системи в jail повинна бути =< версії хоста – перевіряємо її з freebsd-version:
[root@test-free-15-bastille ~]# freebsd-version 15.0-RELEASE
Готуємо “образ” з цією версією:
[root@test-free-15-bastille ~]# bastille bootstrap 15.0-RELEASE Attempting to bootstrap FreeBSD release: 15.0-RELEASE Fetching MANIFEST... /usr/local/bastille/cache/15.0-RELEASE/MANIFES 1044 B 7334 kBps 00s Fetching distfile: base.txz /usr/local/bastille/cache/15.0-RELEASE/base.tx 157 MB 8232 kBps 19s Validating checksum for archive: base.txz MANIFEST: ac0c933cc02ee8af4da793f551e4a9a15cdcf0e67851290b1e8c19dd6d30bba8 DOWNLOAD: ac0c933cc02ee8af4da793f551e4a9a15cdcf0e67851290b1e8c19dd6d30bba8 Extracting archive: base.txz Bootstrap successful.
І маємо нові ZFS datasets:
[root@test-free-15-bastille ~]# zfs list -r zroot/bastille NAME USED AVAIL REFER MOUNTPOINT zroot/bastille 532M 65.9G 120K /usr/local/bastille zroot/bastille/backups 96K 65.9G 96K /usr/local/bastille/backups zroot/bastille/cache 158M 65.9G 96K /usr/local/bastille/cache zroot/bastille/cache/15.0-RELEASE 158M 65.9G 158M /usr/local/bastille/cache/15.0-RELEASE zroot/bastille/jails 96K 65.9G 96K /usr/local/bastille/jails zroot/bastille/logs 96K 65.9G 96K /var/log/bastille zroot/bastille/releases 374M 65.9G 96K /usr/local/bastille/releases zroot/bastille/releases/15.0-RELEASE 374M 65.9G 374M /usr/local/bastille/releases/15.0-RELEASE zroot/bastille/templates 96K 65.9G 96K /usr/local/bastille/templates
Тепер у нас все готове для створення контейнерів – подивимось, як робити jails з FreeBSD та Linux та різними налаштуваннями мережі.
Створення jails
Якщо запускаємо у VirtualBox – включаємо Promiscuous Mode в Allow All:

Створення FreeBSD jails
Спочатку зробимо кілька контейнерів з FreeBSD і різними параметрами мережі – а потім запустимо jail з Linux.
Всі створені jail зберігаються в директорії /usr/local/bastille/jails/ – там для кожного контейнера буде директорія з його ім’ям та файлом jail.conf, який описує параметри цього контейнеру.
Network type VNET
Першим глянемо варіант з VNET – я ним користуюсь найбільше, бо зручно мати прямий доступ в контейнери, плюс повноцінна ізоляція на рівні мережі.
Аби задати тип нетворка VNET – до bastille create передаємо опцію --vnet (або коротка форма -V), потім ім’я jail, версію системи, IP-адресу та інтерфейс хоста для створення bridge.
Інтерфейс можна не передавати, якщо заданий bastille_network_gateway в /usr/local/etc/bastille/bastille.conf.
Замість передачі IP явно – можна вказати опцію DHCP або SYNCDHCP – тоді jail отримає адресу від роутера:
[root@test-free-15-bastille ~]# bastille create --vnet testjailVnetIp 15.0-RELEASE 192.168.0.205/24 em0 Attempting to create jail: testjailVnetIp Valid IP: 192.168.0.205/24 ... [testjailVnetIp]: e0a_bastille1 e0b_bastille1 testjailVnetIp: created
[root@test-free-15-bastille ~]# bastille list JID Name Boot Prio State Type IP Address Published Ports Release Tags 2 testjailVnetIp on 99 Up thin 192.168.0.205 - 15.0-RELEASE -
Deep dive: VNET networking
Трохи детально згадував як працює нетворкінг, в принципі цю частину можна пропустити, але якщо цікаво – то подивимось, як пакет з ноутбука в локальній мережі з FreeBSD-хостом попадає всередину jail.
Тут в прикладі маємо всі хости в одній мережі 192.168.0.0/24:
- робочий ноутбук з Arch Linux
- хост FreeBSD з jail – 192.168.0.72
- і, власне, сам jail з IP 192.168.0.205
Інтерфейси
Перевіряємо інтерфейси на хості FreeBSD:
[root@test-free-15-bastille ~]# ifconfig
em0: [...]
...
ether 08:00:27:d5:55:b2
inet 192.168.0.72 netmask 0xffffff00 broadcast 192.168.0.255
...
em0bridge: [...]
...
ether 58:9c:fc:10:fa:c0
...
member: e0a_bastille1 [...]
...
member: em0 [...]
...
groups: bridge
...
e0a_bastille1: [...]
description: vnet0 host interface for Bastille jail testjailVnetIp
...
ether 02:20:99:d5:55:b2
...
groups: epair
...
Що у нас тепер є:
- інтерфейс
em0:- IP: 192.168.0.72
- MAC: 08:00:27:d5:55:b2
- інтерфейс
em0bridge: L2 switch – передає пакети між своїми members- groups: bridge
- member: em0
- port 1
- member: e0a_bastille1
- port 5
- інтерфейс
e0a_bastille1(з a): host sideepair- groups: epair
- ether 02:20:99:d5:55:b2
А інтерфейс e0b_bastille1 (з b) створюється всередині jail – тільки з іменем vnet0 (для зручності).
Перевіряємо з jexec <jailname> ifconfig:
[root@test-free-15-bastille ~]# jexec testjailVnetIp ifconfig
lo0: [...]
...
vnet0: [...]
description: jail interface for em0
...
ether 0e:20:99:d5:55:b2
...
inet 192.168.0.205 netmask 0xffffff00 broadcast 192.168.0.255
groups: epair
...
Де бачимо, що у vnet0 той самий MAC 0e:20:99:d5:55:b2 як і на хості у інтерфейсів e0a_bastille1 та em0.
Data flow та ARP table
І тепер можна прослідкувати процес передачі даних до jail:
- з ноутбука виконуємо
ssh 192.168.0.205– на jail IP - ноутбук виконує broadcast ARP-запит в мережу 192.168.0.0/24 – “хто має 192.168.0.205?“
- фізичний інтерфейс
em0на хості отримує цей запит, ядро визначає, щоem0– це member bridge-інтерфейсуem0bridgeна port 1, і передає дані наem0bridge em0bridgeпередає його до своїх members, на інші ports – в нашому випадку доe0a_bastille1наport 5e0a_bastille1– це “вхідний” socket, аe0b_bastille1– його “вихід” всередині jail- для аналогії можна згадати
socketpair(), який обєднує два socket, кожен з власним file descritor – на “вході” та на “виході”: все, що записується на “вхідний” сокет – попадає на другий сокет зв’язаної пари
- для аналогії можна згадати
- інтерфейс
vnet0в jail отримує цей запит, відповідає ноутбуку “це мій IP” та повертає свій MAC - ноутбук записує цей MAC в свою ARP table
Подивитись ARP на Arch Linux можемо з ip neigh show:
[setevoy@setevoy-work ~] $ ip neigh show 192.168.0.205 dev enp0s13f0u3u4c2 lladdr 0e:20:99:d5:55:b2 REACHABLE ...
Далі, при формуванні пакету для цього jail – ядро хоста Arch Linux сформує Ethernet frame (див. TCP/IP: моделі OSI та TCP/IP, TCP-пакети, Linux sockets і порти), в якому буде IP packet:
- на OSI layer 2 (Ethernet frame) headers:
- src MAC: MAC інтерфейсу – в моєму випадку enp0s13f0u3u4c2
- dst MAC: 0e:20:99:d5:55:b2 (MAC FreeBSD
em0та jaile0b_bastille1)
- OSI layer 3 (IP packet) headers:
- src IP: IP хоста з Arch Linux
- dst IP: 192.168.0.205 – jail IP
А процес доставки даних в jail виглядає так:
- ноутбук формує Ethernet frame з dst MAC 0e:20:99:d5:55:b2
- фрейм через роутер/свіч домашньої мережі попадає на
em0хоста FreeBSD - ядро FreeBSD “бачить”, що
em0– це member групиem0bridgeта передає дані наe0a_bastille1 - пакет “входить” до
e0a_bastille1– і “виходить” уe0b_bastille1– інтерфейсіvnet0всередині нашого jail - ядро в jail розпаковує Ethernet фрейм, перевіряє dst IP (192.168.0.205) та dst Port (22), бачить, що це його IP, а на порту 22 є демон SSH – і передає IP пакет до SSH
Начебто вірно описав.
Тепер, як трохи розібрались з мережею – можна створювати контейнери далі.
Підключення до jail
Підключитись з хоста можемо з bastille console:
[root@test-free-15-bastille ~]# bastille console testjailVnetIp [testjailVnetIp]: root@testjailVnetIp:~ #
В контейнері запускаємо sshd:
root@testjailVnetIp:~ # sysrc sshd_enable="YES" sshd_enable: NO -> YES root@testjailVnetIp:~ # service sshd start ... Starting sshd.
Додаємо юзера:
root@testjailVnetIp:~ # pw useradd setevoy -m -s /bin/sh root@testjailVnetIp:~ # passwd setevoy
І підключаємось з ноутбука:
[setevoy@setevoy-work ~] $ ssh [email protected] ([email protected]) Password for setevoy@testjailVnetIp: ... setevoy@testjailVnetIp:~ $
Network type Alias/Shared Interface
При Alias/Shared Interface на інтерфейс em0 просто буде доданий другий IP як аліас.
Створюємо контейнер – без додаткових опцій, тільки IP-адресу та інтерфейс хоста, як в прикладі з VNET:
[root@test-free-15-bastille ~]# bastille create testjailAlias 15.0-RELEASE 192.168.0.206 em0 Attempting to create jail: testjailAlias Valid IP: 192.168.0.206 Valid interface: em0 ... [testjailAlias]: testjailAlias: created
Перевіряємо на хості – тепер маємо дві адреси:
[root@test-free-15-bastille ~]# ifconfig
em0: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
...
inet 192.168.0.72 netmask 0xffffff00 broadcast 192.168.0.255
inet 192.168.0.206 netmask 0xffffffff broadcast 192.168.0.206
...
lo0: flags=1008049<UP,LOOPBACK,RUNNING,MULTICAST,LOWER_UP> metric 0 mtu 16384
...
bastille0: flags=8008<LOOPBACK,MULTICAST> metric 0 mtu 16384
...
em0bridge: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
...
e0a_bastille1: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
description: vnet0 host interface for Bastille jail testjailVnetIp
...
А в контейнері – всі ті самі інтерфейси, що і на хості, але для em0 тільки один IP:
[root@test-free-15-bastille ~]# jexec testjailAlias ifconfig
em0: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
...
ether 08:00:27:d5:55:b2
inet 192.168.0.206 netmask 0xffffffff broadcast 192.168.0.206
...
lo0: flags=1008049<UP,LOOPBACK,RUNNING,MULTICAST,LOWER_UP> metric 0 mtu 16384
...
bastille0: flags=8008<LOOPBACK,MULTICAST> metric 0 mtu 16384
...
em0bridge: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
...
e0a_bastille1: flags=1008943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
...
Тепер, якщо ми не запустимо sshd в контейнері – то підключення на IP 192.168.0.206 піде на SSH daemon самого хоста – “Password for setevoy@test-free-15-bastille”
[setevoy@setevoy-work ~] $ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null [email protected] Warning: Permanently added '192.168.0.206' (ED25519) to the list of known hosts. ([email protected]) Password for setevoy@test-free-15-bastille:
А якщо маємо відкритий порт 22 в контейнері:
root@testjailAlias:~ # service sshd onestart
То запит піде на нього – “Password for setevoy@testjailAlias“:
[setevoy@setevoy-work ~] $ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null [email protected] Warning: Permanently added '192.168.0.206' (ED25519) to the list of known hosts. ([email protected]) Password for setevoy@testjailAlias:
Простіше за VNET – але маємо загальні правила Packet Filter, фаервола на хості, нема можливості отримати адресу з DHCP, можливі проблеми з overlapping ports, і важливе: якщо ламають наш jail – то отримують доступ до всієї мережі хоста.
Network type NAT
І останній на сьогодні приклад – з NAT, тільки тепер задаємо IP не з пулу домашньої мережі, а в інтерфейсі вказуємо loopback інтерфейс хоста bastille0:
[root@test-free-15-bastille ~]# bastille create testjailNat 15.0-RELEASE 10.0.0.10 bastille0 Attempting to create jail: testjailNat Valid IP: 10.0.0.10 Valid interface: bastille0 ...
Перевіряємо джейли тепер:
[root@test-free-15-bastille ~]# bastille list JID Name Boot Prio State Type IP Address Published Ports Release Tags 4 testjailAlias on 99 Up thin 192.168.0.206 - 15.0-RELEASE - 6 testjailNat on 99 Up thin 10.0.0.10 - 15.0-RELEASE - 2 testjailVnetIp on 99 Up thin 192.168.0.205 - 15.0-RELEASE -
Роутинг пакетів до jail піде через Packet Filter:
[root@test-free-15-bastille ~]# pfctl -s nat nat on em0 from <jails> to any -> (em0:0) rdr-anchor "rdr/*" all
Запускаємо SSH в контейнері:
[root@test-free-15-bastille ~]# bastille service testjailNat sshd onestart [testjailNat]: Generating RSA host key. ... Starting sshd.
Маємо підключення з хоста на 10.0.0.10:
[root@test-free-15-bastille ~]# ssh 10.0.0.10 ... ([email protected]) Password for root@testjailNat:
А для підключення із зовнішньої мережі – на хості вмикаємо port forwarding (bastille rdr – redirect через Packer Filter):
[root@test-free-15-bastille ~]# bastille rdr testjailNat tcp 2222 22 IPv4 tcp/2222:22 on em0
І підключаємось з SSH на IP хоста FreeBSD, але порт задаємо 2222, і попадаємо в новий Jail – “Password for setevoy@testjailNat“:
[setevoy@setevoy-work ~] $ ssh -p 2222 [email protected] ... ([email protected]) Password for setevoy@testjailNat:
І на останок – jails з Linux.
Створення Linux Jails
Документація – Linux Jails.
Важливе обмеження Linux jail – для мережі недоступні VNET-опції. Тобто з варіантів – або NAT і port-forward, або Alias з усіма його обмеженнями і можливими проблемами.
Крім того – “Linux jails are still considered experimental” – хоча в цілому працює достатньо стабільно.
Для Linux нам потрібно виконати bastille setup linux – тоді Bastille підтягне потрібні модулі і скрипти:
[root@test-free-15-bastille ~]# bastille setup linux [WARNING]: Running linux jails requires loading additional kernel modules, as well as installing the 'debootstrap' package. Do you want to proceed with setup? [y|n]:y Loading kernel module: fdescfs Persisting module: fdescfs fdescfs_load: -> YES Loading kernel module: linprocfs Persisting module: linprocfs linprocfs_load: -> YES Loading kernel module: linsysfs Persisting module: linsysfs linsysfs_load: -> YES Loading kernel module: linux Loading kernel module: linux64 linux_enable: NO -> YES ...
Тепер в директорії /usr/local/share/debootstrap/scripts/ маємо набір shell-скриптів, які налаштують Linux-оточення:
[root@test-free-15-bastille ~]# less /usr/local/share/debootstrap/scripts/gutsy
case $ARCH in
amd64|i386)
case $SUITE in
gutsy|hardy|intrepid|jaunty|karmic|lucid|maverick|natty|oneiric|precise|quantal|raring|saucy|utopic|vivid|wily|yakkety|zesty)
default_mirror http://old-releases.ubuntu.com/ubuntu
...
keyring /usr/local/share/keyrings/ubuntu-archive-keyring.gpg
Виконуємо bootstrap, вказуємо ім’я системи – власне, ім’я скрипта з /usr/local/share/debootstrap/scripts/.
Але для Ubuntu остання доступна версія – Jammy, 22.04.
Займе хвилин 10-15, може і більше поки все скачає:
[root@test-free-15-bastille ~]# bastille bootstrap jammy Attempting to bootstrap Linux/Ubuntu release: Ubuntu_2204 Ensuring Linux compatability... ...
Потім створюємо контейнер з create та опцією --linux (-L):
[root@test-free-15-bastille ~]# bastille create -L openwebui jammy 192.168.0.207/24 em0 ... [openwebui]: openwebui: created
І маємо контейнер з “Ubuntu”:
[root@test-free-15-bastille ~]# bastille cmd openwebui lsb_release -a [openwebui]: No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04 LTS
Bastille та Jails management – основні команди
І трохи про основні доступні команди для роботи з контейнерами, див. також приклади у FreeBSD Jails with Bastille – свіжий, 2025 року.
Документація – Bastille sub-commands.
clone: скопіювати jail (див. Limitations – є нюанси з інтерфейсами)cmd: виконати команду в jail:
[root@test-free-15-bastille ~]# bastille cmd testjailNat ps [hermesagent1]: PID TT STAT TIME COMMAND 63825 2 R+J 0:00.00 ps
config: отримати чи змінити параметр :
[root@test-free-15-bastille ~]# bastille config testjailVnetIp get vnet.interface e0b_bastille1
cp: скопіювати файл з хоста в jaildestroy: видалити jail та всі його даніexport: створити архів в усіма даними jail, потім import можна відновити на іншому хостіmount: підключити файл чи каталог з хоста в контейнерrestart: перезапустити jailtop,htop: ресурси та процеси в контейнері
Також варто подивитись на bastille monitor – є цікаві можливості з моніторингу і алертингу, та Templates – створення контейнерів з шаблонів які можна взяти з BastilleBSD/templates або створювати власні.
А з bastille zfs – можна створювати ZFS snapshots контейнерів (див. FreeBSD: Home NAS, part 5 – ZFS pool, datasets, snapshots та моніторинг).
![]()