FreeBSD jails frente a contenedores Docker
Compare FreeBSD jails y Docker: userland completo frente a imágenes por capas, con diferencias prácticas en software, estado, red y límites.
FreeBSD jails frente a contenedores Docker, en un párrafo
FreeBSD jails y los contenedores Docker resuelven el mismo problema de dos formas distintas. Ambos ejecutan userlands aislados sobre un kernel compartido, por lo que ninguno es una máquina virtual. La diferencia está en lo que contienen. Un contenedor Docker ejecuta un proceso a partir de una imagen por capas que se obtiene de un registro. Un jail ejecuta un userland completo de FreeBSD: su propio /etc, sus propios scripts de inicio de rc, su propia base de datos pkg y tantos procesos como necesite. Casi todas las demás diferencias de esta página se derivan de ese único hecho.
SSD Nodes no ofrece imágenes de FreeBSD. No puede alquilar un servidor FreeBSD en esta plataforma, y nada de lo que aparece a continuación es una guía para instalarlo en una máquina que pueda comprar aquí. Esta es una comparación de dos modelos de aislamiento. Está escrita para que pueda determinar qué modelo necesita realmente una carga de trabajo y leer la configuración de un equipo de FreeBSD sin tener que adivinar.
Qué es realmente un jail
Los jails llegaron a FreeBSD 4.0 en marzo de 2000, por lo que son anteriores a cgroups y aproximadamente una década anteriores a Docker. El mecanismo consiste en una llamada al kernel. jail(8) toma un árbol de directorios e inicia procesos dentro de él con un ID de jail asociado. Después, el kernel rechaza un conjunto definido de operaciones para cualquier proceso que tenga ese ID. Un proceso dentro de un jail no puede ver los procesos que están fuera de su jail, montar ni desmontar sistemas de archivos, cargar módulos del kernel ni asociarse a direcciones de red que no se hayan asignado al jail. No hay que aprender otro tipo de namespace ni activar cada función por separado: las restricciones se aplican como una unidad y se ajustan mediante parámetros en la configuración del jail.
En el host, jls muestra los jails en ejecución y jexec web sh abre un shell dentro del jail llamado web.
Para crear un jail, coloque un userland de FreeBSD en un directorio. El sistema base lo hace por usted:
sudo bsdinstall jail /usr/local/jails/containers/webEsto descarga el conjunto de distribución base correspondiente a su release y ejecuta los pasos normales posteriores a la instalación. Por tanto, establezca una contraseña de root y elija una zona horaria exactamente como lo haría en un servidor nuevo. El resultado es una instalación de FreeBSD ubicada en un directorio. Después, descríbala en /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;
}Iníciela y compruebe su estado:
sudo service jail start web
jlsjls debería mostrar ahora web con un JID, su hostname y su dirección IP. Si el jail no aparece, ejecute sudo jail -c web directamente. Aplica la misma configuración en primer plano e indica el parámetro que no pudo aceptar, en lugar de dejar el error en la salida del servicio.
La línea que conviene leer dos veces es exec.start = "/bin/sh /etc/rc". Al iniciar un jail se ejecuta dentro de él el script de arranque normal de FreeBSD. Por tanto, el jail inicia todos los servicios habilitados en su propio /etc/rc.conf. Un contenedor Docker no tiene un paso equivalente, porque ejecuta el proceso entrypoint de la imagen y se detiene cuando ese proceso se detiene.
Cómo se obtiene el software: imágenes y registros frente a un userland que se configura
Esta es la diferencia que se nota desde el primer día.
Con Docker se indica el software y se recibe. docker pull nginx obtiene una imagen por capas e identificada por contenido que otra persona compiló y probó, y docker compose up -d la inicia con sus volúmenes y su red conectados. El registro es el producto. Gran parte del valor de un flujo de trabajo con Docker consiste en que miles de proyectos publican una imagen funcional. Esto hace que ejecutar Docker en un VPS sea una tarea breve y no un proyecto.
FreeBSD no incluye un registro público predeterminado de imágenes de jail. Se crea un userland vacío y se instala el software en él, igual que al configurar un servidor básico. Esto requiere escribir más comandos. También es más transparente, porque lo que se ejecuta en la jail es lo que pkg instaló, a partir del mismo conjunto de paquetes que usa el host.
Las herramientas simplifican el proceso. BastilleBSD es el gestor de jail habitual y se instala como paquete:
sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASEbastille setup configura la red, el almacenamiento y el firewall. bastille bootstrap descarga una release una sola vez, y todas las jail que se creen después la reutilizan. FreeBSD 15.1 es la release actual para producción, publicada en junio de 2026; sustituya la release que utilice.
Crear una jail requiere un comando, y configurarla requiere uno más:
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 proporciona un shell de inicio de sesión dentro de la jail, y bastille list muestra lo que existe en el host. Para repetir una compilación, las plantillas de Bastille contienen los pasos en un archivo y los aplican a una jail. Es lo más parecido a un Dockerfile en este entorno. La plantilla se reproduce en cada jail. Nada llega precompilado.
El resumen es sencillo. Docker entrega compilaciones de otras personas. Las jail proporcionan instalaciones propias. Si el software de su lista sólo se publica como imagen de contenedor, la decisión queda resuelta antes de que cualquier otro criterio pueda influir.
Estado y actualizaciones: la parte que cambia con ZFS
Docker separa el estado de forma deliberada. El sistema de archivos del contenedor es desechable, los datos se almacenan en un volumen con nombre o en un bind mount, y una actualización consiste en docker compose pull seguida de docker compose up -d. El contenedor se reemplaza y todo lo que no se haya almacenado en un volumen desaparece. Esto es una ventaja cuando se sigue la regla y un incidente de pérdida de datos cuando se olvida. Por eso la elección entre bind mounts y volúmenes con nombre es tan importante en una pila de Compose.
Un jail no separa el estado, y ZFS es lo que permite este funcionamiento. Todo el jail es un dataset:
sudo zfs snapshot zroot/jails/containers/web@pre-upgrade
sudo pkg -j web upgrade
sudo zfs rollback zroot/jails/containers/web@pre-upgradeCompruebe el nombre real del dataset con zfs list antes de ejecutar esto. La ruta anterior corresponde a la disposición que utiliza el manual. La creación de la instantánea tarda aproximadamente un segundo y apenas consume espacio hasta que cambia el contenido del jail. Si la actualización rompe el servicio, el rollback devuelve todo el userland a su estado anterior, incluida la base de datos de paquetes y los archivos de configuración que editó manualmente a las 2am. Docker no tiene un equivalente integrado, porque su modelo presupone que nunca necesitó uno.
zfs clone es la otra mitad. Un clon de una instantánea es un jail nuevo y escribible que comparte con su padre los bloques que no han cambiado. Por eso, una copia de staging de un jail de 3 GB apenas consume espacio en disco hasta que empieza a modificarla. Así crea un administrador de FreeBSD un jail "igual que producción" para ensayar una actualización.
La actualización del sistema base es independiente de los paquetes. Para un jail que contiene su propia copia del userland:
sudo freebsd-update -b /usr/local/jails/containers/web fetch installLos thin jails evitan repetir ese trabajo. Montan una única base compartida de solo lectura mediante nullfs y proporcionan a cada jail su propia capa pequeña y escribible. Así se aplica el parche a la base una sola vez y todos los jails ven el resultado. Bastille crea thin jails de forma predeterminada.
Redes: puertos publicados frente a una decisión de direccionamiento
Docker decide la red por usted y le pide que publique las excepciones. Los contenedores se conectan a un bridge, se alcanzan entre sí mediante el nombre del servicio en una red definida por el usuario y -p 8080:80 expone uno de ellos al host. Docker escribe sus propias reglas de filtrado de paquetes para conseguirlo. Así es también como un puerto de contenedor publicado pasa directamente por alto ufw.
Un jail le obliga a elegir el modelo desde el principio, y hay dos.
IP compartida. ip4.addr = "10.0.0.10" añade esa dirección a una interfaz existente del host y restringe el jail a ella. El jail no tiene su propia pila de red, por lo que no puede ejecutar su propio firewall. Tampoco puede enlazarse realmente a todas las direcciones: un socket del jail que solicite 0.0.0.0 es reescrito por el kernel con la dirección propia del jail. Dos jails no pueden escuchar a la vez en el puerto 80 de la misma dirección. Por tanto, debe asignar una dirección a cada uno o colocar un reverse proxy delante.
VNET. Añada vnet; al jail para que obtenga una pila de red completa: sus propias interfaces, su propia tabla de enrutamiento y sus propias reglas de firewall. Conéctelo al host mediante un epair, un cable virtual con un extremo en cada lado, y coloque el extremo del host en un bridge. Este es el modelo más parecido al que proporciona Docker y es el modo que usan los tipos de jail -V y -B de Bastille.
El reenvío de un puerto del host a un jail se realiza mediante una regla de redirección pf. Bastille la encapsula:
sudo bastille rdr web tcp 80 80No existe EXPOSE ni una publicación automática. Nada llega a un jail a menos que su dirección o una regla de redirección lo permita. El arranque es más lento y el firewall queda mucho más silencioso.
Límites de recursos: cgroups frente a rctl
Docker limita un contenedor con cgroups, y los límites se definen junto al contenedor: --memory=1g --cpus=1.5 en la línea de comandos o las claves correspondientes en un archivo Compose. Si ya mantiene la pila en un archivo Docker Compose en un VPS, el límite queda junto al servicio al que se aplica y se conserva con él en git.
FreeBSD utiliza rctl, un subsistema que debe activar. La contabilidad de recursos está desactivada de forma predeterminada porque añade un pequeño coste a cada asignación. Añada el parámetro ajustable a /boot/loader.conf y reinicie:
kern.racct.enable=1Después, establezca una regla y monitorícela:
sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:webrctl -hu jail:web muestra el uso actual del jail en unidades legibles, para que pueda ver cuánto falta para alcanzar el límite antes de que algo falle. La acción deny hace que la asignación que supera el límite falle dentro del jail. Así verá el error de asignación de la propia aplicación en lugar de un mensaje de terminación en el host.
Las reglas añadidas con rctl -a desaparecen en el siguiente reinicio. El servicio rctl de FreeBSD las vuelve a cargar desde /etc/rctl.conf. Escriba la regla en ese archivo y active el servicio:
sudo sysrc rctl_enable=YESEsta es la diferencia en la que Docker resulta claramente más práctico. Un límite en un archivo Compose se revisa junto con el servicio al que restringe. Una regla de rctl es una línea en un archivo independiente que nombra un jail definido en otro lugar.
Cuando la respuesta es una máquina virtual: bhyve
Un jail comparte el kernel del host, por lo que algunas funciones quedan permanentemente fuera de su alcance. No puede ejecutar una versión diferente del kernel, cargar un módulo del kernel ni ejecutar binarios de Linux como lo hace un contenedor Linux. FreeBSD tiene una capa de compatibilidad con Linux, linuxulator, pero implementa un subconjunto de las llamadas al sistema de Linux y no es una solución general para imágenes Linux arbitrarias.
bhyve es el hipervisor de FreeBSD y es la herramienta adecuada cuando necesita un límite real entre máquinas: otro sistema operativo, otro kernel o un tenant con el que prefiera no compartir el kernel. El coste es reservar memoria en lugar de compartirla y mantener un segundo kernel con sus parches. Es la misma decisión que se toma en Linux entre contenedores y máquinas virtuales completas, y determina si necesita un VPS compatible con la virtualización anidada por debajo.
El ecosistema, que es la razón real por la que la mayoría de los equipos usa Docker
Todo lo anterior trata sobre el modelo. Para la mayoría de los equipos, la decisión depende del tamaño del ecosistema que rodea cada opción.
Docker ofrece Docker Hub y GHCR, docker compose, Kubernetes cuando un solo equipo deja de ser suficiente, ejecutores de CI con compatibilidad con contenedores ya integrada y un inicio rápido con un solo comando en el README de casi todos los proyectos. Los jails ofrecen el árbol de ports de FreeBSD, que es amplio y se mantiene con cuidado, además de un conjunto mucho más reducido de paquetes de aplicaciones listos para ejecutar. Cuando un proyecto publica una imagen de contenedor y nada más, la opción en FreeBSD consiste en leer su documentación y ensamblar las piezas manualmente.
Los jails justifican su uso en el otro lado de esa disyuntiva. Son adecuados cuando ya utiliza ZFS y valora las instantáneas y la reversión de un servicio completo, cuando sus servicios son nativos de FreeBSD, cuando quiere un userland completo por tenant en lugar de un único proceso o cuando quiere que el kernel, el filtro de paquetes, el sistema de archivos y la documentación se mantengan juntos como un sistema único. Este último punto es lo que se quiere decir al describir FreeBSD como un sistema coherente, y se desarrolla con más detalle en la comparación amplia entre Linux y FreeBSD como plataformas de servidor y en los cambios de FreeBSD 15 para el uso en servidores.
Una última conclusión. Si su equipo ya conoce Docker, el coste de cambiar es real y el beneficio debe ser concreto. No cambie por la calidad del aislamiento; los dos modelos son suficientemente similares y su configuración tiene más importancia. Cambie porque quiere reversión de servicios completos respaldada por ZFS o porque ya utiliza FreeBSD.
FAQ
¿Puedo ejecutar imágenes de Docker en FreeBSD?
No imágenes de Linux ni mediante un método compatible oficialmente. FreeBSD sí admite contenedores OCI: sudo pkg install -y podman-suite instala Podman, que ejecuta contenedores mediante ocijail, un runtime que crea jails reales por debajo. Necesita fdescfs montado en /dev/fd para el monitor de contenedores, y pf para la NAT de contenedores (traducción de direcciones de red). Las imágenes OCI nativas de FreeBSD funcionan mejor. Las imágenes de Linux también requieren la capa de compatibilidad con Linux y, en agosto de 2026, el port de Podman para FreeBSD todavía se describe como experimental. Si su despliegue es una pila de imágenes de Linux, ejecútelo en Linux. En la familia RHEL, eso significa instalar Docker en Rocky Linux o AlmaLinux, donde Podman aparece de nuevo como el paquete que ya proporciona el comando docker antes de instalar nada.
¿Son los jails de FreeBSD más seguros que los contenedores de Docker?
Ambos comparten el kernel del host, por lo que un fallo del kernel supone un riesgo para los dos. Ninguno es el límite de aislamiento que elegiría para código realmente no confiable. La diferencia está en el punto de partida. Un jail comienza con un conjunto amplio de operaciones rechazadas y usted las vuelve a habilitar parámetro por parámetro. Un contenedor de Docker comienza como root dentro de un conjunto de namespaces, con algunas capabilities eliminadas, y el endurecimiento adicional es opcional. En la práctica, la configuración influye más que el modelo: un jail con allow.mount y allow.raw_sockets habilitados no es más seguro que un contenedor configurado cuidadosamente.
¿Cómo hago una copia de seguridad de un jail?
Cree una snapshot del dataset y envíela. sudo zfs snapshot zroot/jails/containers/web@backup y, después, zfs send esa snapshot a otro pool o a un archivo que copie fuera del servidor. Como un jail mantiene todo su userland en un solo dataset, la snapshot captura los paquetes instalados y los datos en un punto coherente, junto con todos los archivos de configuración que haya editado manualmente. Esto es lo contrario del método habitual con Docker, donde se hacen copias de seguridad de los named volumes y del archivo Compose, y se reconstruye el resto a partir de la imagen.
¿Necesito BastilleBSD o es suficiente el sistema base?
El sistema base es suficiente y es el mejor punto de partida. jail.conf, jls, jexec y service jail start cubren todo el modelo. Cuando los conozca, podrá leer cualquier host FreeBSD sin tener que aprender primero las herramientas específicas de ese host. Bastille es una capa de conveniencia sobre el sistema base: inicializa releases, crea jails ligeros, aplica plantillas y escribe las reglas de redirección pf por usted. Aprenda primero los comandos base y añada Bastille cuando el número de jails haga tediosa la escritura manual.