Jails FreeBSD ou conteneurs Docker : quelles différences ?
Comparez les jails FreeBSD et Docker : userland complet contre images en couches, avec les différences concrètes sur les logiciels, l’état, le réseau et les limites.
Jails FreeBSD et conteneurs Docker : comparaison en un paragraphe
Les jails FreeBSD et les conteneurs Docker répondent au même besoin, mais selon deux modèles différents. Tous deux exécutent des environnements utilisateur isolés sur un même kernel partagé ; aucun des deux n’est donc une machine virtuelle. La différence tient à ce qu’ils contiennent. Un conteneur Docker exécute un processus depuis une image en couches récupérée dans un registry. Une jail exécute un environnement utilisateur FreeBSD complet : son propre /etc, ses propres scripts de démarrage rc, sa propre base pkg et autant de processus que nécessaire. Presque toutes les autres différences présentées sur cette page découlent de ce point.
SSD Nodes ne propose pas d’images FreeBSD. Vous ne pouvez pas louer de serveur FreeBSD sur cette plateforme, et le contenu ci-dessous n’est pas un guide d’installation pour une machine que vous pourriez y acheter. Il s’agit d’une comparaison de deux modèles d’isolation, conçue pour vous aider à déterminer lequel convient réellement à une charge de travail et à lire la configuration d’une équipe FreeBSD sans avoir à la deviner.
Ce qu’est réellement une jail
Les jails sont apparues dans FreeBSD 4.0 en mars 2000. Elles sont donc antérieures aux cgroups et datent d’environ dix ans avant Docker. Le mécanisme repose sur un appel au noyau. jail(8) prend une arborescence de répertoires et démarre les processus à l’intérieur avec un identifiant de jail associé. Le noyau refuse ensuite un ensemble défini d’opérations à tout processus portant cet identifiant. Un processus dans une jail ne peut pas voir les processus situés en dehors de sa jail. Il ne peut pas monter ou démonter de systèmes de fichiers, charger de modules du noyau ni se lier à des adresses réseau qui n’ont pas été attribuées à la jail. Il n’existe pas de type d’espace de noms distinct à apprendre ni d’activation fonctionnalité par fonctionnalité. Les restrictions s’appliquent en bloc et sont ajustées par les paramètres de configuration de la jail.
Sur l’hôte, jls liste les jails en cours d’exécution et jexec web sh vous ouvre un shell dans celle nommée web.
Vous créez une jail en plaçant un userland FreeBSD dans un répertoire. Le système de base s’en charge pour vous :
sudo bsdinstall jail /usr/local/jails/containers/webCette commande récupère le jeu de distribution de base correspondant à votre release et exécute les étapes post-installation habituelles. Vous définissez donc un mot de passe root et choisissez un fuseau horaire, exactement comme sur un nouveau serveur. Le résultat est une installation FreeBSD placée dans un répertoire. Vous la décrivez ensuite dans /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;
}Démarrez-la, puis vérifiez son état :
sudo service jail start web
jlsjls doit maintenant afficher web avec un JID, son hostname et son adresse IP. Si la jail n’apparaît pas, exécutez directement sudo jail -c web. Cette commande applique la même configuration au premier plan et affiche le paramètre qu’elle n’a pas pu accepter, au lieu de laisser l’erreur dans la sortie du service.
La ligne à relire est exec.start = "/bin/sh /etc/rc". Le démarrage d’une jail exécute le script de boot normal de FreeBSD à l’intérieur de celle-ci. La jail démarre donc chaque service activé dans son propre /etc/rc.conf. Un conteneur Docker n’a pas d’étape équivalente, car il exécute le processus d’entrypoint de l’image et s’arrête lorsque ce processus s’arrête.
Comment obtenir les logiciels : images et registres contre userland à remplir
C’est la différence que vous constatez dès le premier jour.
Avec Docker, vous indiquez un logiciel et vous le recevez. docker pull nginx récupère une image en couches, adressée par son contenu, qu’une autre personne a construite et testée, puis docker compose up -d la démarre avec ses volumes et son réseau attachés. Le registre est le produit. Une grande partie de la valeur d’un workflow Docker vient du fait que des milliers de projets publient une image fonctionnelle. C’est ce qui permet de faire fonctionner Docker sur un VPS rapidement, plutôt que d’en faire un projet.
FreeBSD ne fournit pas de registre public par défaut pour les images de jail. Vous créez un userland vide et vous y installez les logiciels, comme vous le feriez sur un serveur vierge. Cela demande davantage de commandes. C’est aussi plus transparent, car ce qui s’exécute dans la jail correspond à ce que pkg y a installé, à partir du même jeu de paquets que celui utilisé par l’hôte.
Les outils permettent de réduire ces étapes. BastilleBSD est le gestionnaire de jail courant et c’est un paquet :
sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASEbastille setup configure le réseau, le stockage et le pare-feu pour vous. bastille bootstrap télécharge une release une seule fois, puis chaque jail créée ensuite la réutilise. FreeBSD 15.1 est la release de production actuelle, publiée en juin 2026 ; remplacez-la par la release que vous utilisez.
La création d’une jail se fait alors avec une commande, puis son remplissage avec une autre :
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 vous fournit un shell de connexion dans la jail, et bastille list affiche ce qui existe sur l’hôte. Pour reproduire une build, les templates Bastille contiennent les étapes dans un fichier et les appliquent à une jail. C’est ce qui se rapproche le plus d’un Dockerfile dans cet environnement. Un template est rejoué sur chaque jail. Rien n’arrive préinstallé.
Le résumé est donc simple. Docker vous fournit les builds d’autres personnes. Les jails vous permettent de réaliser vos propres installations. Si le logiciel de votre liste n’est disponible que sous forme d’image de conteneur, la question est réglée avant même d’examiner les autres critères.
État et mises à niveau : la partie que ZFS change
Docker sépare volontairement l’état. Le système de fichiers du conteneur est jetable, vos données résident dans un volume nommé ou un bind mount, et une mise à niveau consiste à docker compose pull puis à docker compose up -d. Le conteneur est remplacé, et tout ce que vous n’avez pas placé dans un volume est perdu. C’est un avantage si vous respectez cette règle, mais un incident de perte de données si vous l’oubliez. C’est pourquoi le choix entre les bind mounts et les volumes nommés est si important dans une stack Compose.
Une jail ne sépare pas l’état, et c’est ZFS qui permet ce fonctionnement. Toute la jail constitue un dataset :
sudo zfs snapshot zroot/jails/containers/web@pre-upgrade
sudo pkg -j web upgrade
sudo zfs rollback zroot/jails/containers/web@pre-upgradeVérifiez le nom réel du dataset avec zfs list avant d’exécuter cette commande ; le chemin ci-dessus correspond à l’organisation utilisée par le handbook. Le snapshot prend environ une seconde et consomme presque aucun espace tant que le contenu de la jail ne change pas. Si la mise à niveau casse le service, le rollback restaure tout l’environnement utilisateur dans son état précédent, y compris la base de données des packages et les fichiers de configuration que vous avez modifiés manuellement à 2 h du matin. Docker n’a pas d’équivalent intégré, car son modèle suppose que vous n’en aurez jamais besoin.
zfs clone constitue l’autre élément. Un clone d’un snapshot est une nouvelle jail accessible en écriture qui partage les blocs inchangés avec sa source. Une copie de staging d’une jail de 3 GB ne coûte donc presque rien sur le disque tant que vous ne commencez pas à la modifier. C’est ainsi qu’un administrateur FreeBSD crée une jail « identique à la production » pour tester une mise à niveau.
La mise à niveau du système de base est distincte de celle des packages. Pour une jail qui contient sa propre copie de l’environnement utilisateur :
sudo freebsd-update -b /usr/local/jails/containers/web fetch installLes jails thin évitent de répéter cette opération. Elles montent une base partagée en lecture seule via nullfs et fournissent à chaque jail sa propre petite couche accessible en écriture. Vous mettez ainsi la base à jour une seule fois, et toutes les jails voient le résultat. Bastille crée des jails thin par défaut.
Réseau : ports publiés ou choix d’adressage
Docker définit le réseau à votre place et vous demande de publier les exceptions. Les conteneurs sont placés sur un bridge, communiquent entre eux par nom de service sur un réseau défini par l’utilisateur, et -p 8080:80 expose l’un d’eux sur l’hôte. Docker écrit ses propres règles de filtrage de paquets pour y parvenir. C’est aussi pourquoi un port de conteneur publié contourne directement ufw.
Avec un jail, vous devez choisir le modèle dès le départ. Il en existe deux.
IP partagée. ip4.addr = "10.0.0.10" ajoute cette adresse à une interface existante de l’hôte et limite le jail à cette adresse. Le jail n’a pas sa propre pile réseau et ne peut donc pas exécuter son propre firewall. Il ne peut pas non plus réellement s’attacher à toutes les adresses : un socket du jail qui demande 0.0.0.0 est réécrit par le kernel avec l’adresse propre au jail. Deux jails ne peuvent pas écouter simultanément sur le port 80 de la même adresse. Vous devez donc attribuer une adresse à chacun ou placer un reverse proxy devant eux.
VNET. Ajoutez vnet; au jail pour lui fournir une pile réseau complète : ses propres interfaces, sa propre table de routage et ses propres règles de firewall. Reliez-le à l’hôte avec un epair, c’est-à-dire un câble virtuel dont une extrémité se trouve de chaque côté, puis placez l’extrémité côté hôte sur un bridge. C’est le modèle qui correspond le mieux à celui de Docker. C’est aussi le mode utilisé par les types de jail -V et -B de Bastille.
La redirection d’un port de l’hôte vers un jail est une règle de redirection pf. Bastille l’encapsule :
sudo bastille rdr web tcp 80 80Il n’y a ni EXPOSE ni publication automatique. Rien n’atteint un jail tant que son adresse ou une règle de redirection ne l’autorise pas. Le démarrage est donc plus lent, mais le firewall est beaucoup plus silencieux.
Limites de ressources : cgroups ou rctl
Docker limite un conteneur avec des cgroups, et les limites sont définies au même endroit que le conteneur : --memory=1g --cpus=1.5 sur la ligne de commande, ou les clés correspondantes dans un fichier Compose. Si vous gérez déjà votre stack dans un fichier Docker Compose sur un VPS, la limite se trouve à côté du service auquel elle s’applique et est versionnée avec lui dans git.
FreeBSD utilise rctl, un sous-système que vous devez activer. La comptabilisation des ressources est désactivée par défaut, car elle ajoute un faible coût à chaque allocation. Ajoutez le paramètre dans /boot/loader.conf, puis redémarrez :
kern.racct.enable=1Définissez ensuite une règle et surveillez-la :
sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:webrctl -hu jail:web affiche l’utilisation actuelle du jail dans des unités lisibles, afin de voir à quelle distance il se trouve de la limite avant qu’un problème ne survienne. L’action deny fait échouer l’allocation qui dépasse la limite à l’intérieur du jail. Vous obtenez ainsi l’erreur d’allocation de l’application, au lieu d’un message indiquant qu’un processus a été tué sur l’hôte.
Les règles ajoutées avec rctl -a disparaissent au prochain redémarrage. Le service rctl de FreeBSD les recharge depuis /etc/rctl.conf. Écrivez donc la règle dans ce fichier, puis activez le service :
sudo sysrc rctl_enable=YESC’est sur ce point que Docker est nettement plus pratique. Une limite définie dans un fichier Compose est examinée avec le service qu’elle contraint. Une règle rctl est une ligne dans un fichier distinct qui nomme un jail défini ailleurs.
Lorsqu’il faut une machine virtuelle : bhyve
Une jail partage le kernel de l’hôte, ce qui rend certaines fonctions définitivement inaccessibles. Elle ne peut pas exécuter une autre version du kernel, charger un kernel module ni exécuter des binaires Linux comme le fait un conteneur Linux. FreeBSD dispose d’une couche de compatibilité Linux, linuxulator, mais celle-ci n’implémente qu’un sous-ensemble des appels système Linux. Elle ne constitue donc pas une solution générale pour des images Linux arbitraires.
bhyve est l’hyperviseur de FreeBSD. C’est le bon outil lorsque vous avez besoin d’une véritable séparation entre machines : pour exécuter un autre système d’exploitation, un autre kernel ou un tenant avec lequel vous préférez ne pas partager le kernel. En contrepartie, la mémoire est réservée au lieu d’être partagée, et vous devez maintenir un second kernel à jour. C’est le même choix que sur Linux entre les conteneurs et les machines virtuelles complètes. Il détermine aussi si vous avez besoin de un VPS qui prend en charge la virtualisation imbriquée en dessous.
L’écosystème, qui est la vraie raison pour laquelle la plupart des équipes utilisent Docker
Tout ce qui précède concerne le modèle. Pour la plupart des équipes, le choix dépend surtout de l’écosystème qui entoure chaque solution.
Docker fournit Docker Hub et GHCR, docker compose, Kubernetes lorsqu’une seule machine ne suffit plus, des runners CI avec la prise en charge des conteneurs déjà intégrée, ainsi qu’un démarrage rapide en une commande dans le README de presque chaque projet. Les jails donnent accès à l’arbre de ports de FreeBSD, vaste et soigneusement maintenu, ainsi qu’à un ensemble beaucoup plus restreint de bundles applicatifs prêts à l’emploi. Lorsqu’un projet publie une image de conteneur et rien d’autre, la solution FreeBSD consiste à lire sa documentation et à assembler vous-même les composants.
Les jails deviennent pertinentes de l’autre côté de cet arbitrage. Elles conviennent si vous utilisez déjà ZFS et accordez de l’importance aux snapshots et au rollback d’un service complet, si vos services sont natifs FreeBSD, si vous voulez un userland complet par tenant plutôt qu’un processus unique, ou si vous voulez que le kernel, le packet filter, le système de fichiers et la documentation soient maintenus ensemble comme un seul système. C’est ce que l’on veut dire lorsque l’on qualifie FreeBSD de cohérent. Ce point est développé dans la comparaison plus large de Linux et FreeBSD comme plateformes serveur et dans les changements apportés par FreeBSD 15 pour les serveurs.
Un dernier constat. Si votre équipe connaît déjà Docker, le coût d’une migration est réel et le bénéfice attendu doit être précis. Ne changez pas de solution pour améliorer l’isolation ; les deux modèles sont suffisamment proches pour que votre configuration ait davantage d’importance. Changez parce que vous voulez un rollback de services complets reposant sur ZFS, ou parce que vous utilisez déjà FreeBSD.
FAQ
Puis-je exécuter des images Docker sur FreeBSD ?
Pas des images Linux, et pas avec une prise en charge officielle. FreeBSD prend bien en charge les conteneurs OCI : sudo pkg install -y podman-suite installe Podman, qui exécute les conteneurs via ocijail, un runtime qui crée de véritables jails sous-jails. Il a besoin de fdescfs monté sur /dev/fd pour le moniteur de conteneurs, et de pf pour le NAT des conteneurs (network address translation). Les images OCI natives FreeBSD fonctionnent le mieux. Les images Linux nécessitent en plus la couche de compatibilité Linux et, en août 2026, le port Podman de FreeBSD est toujours décrit comme expérimental. Si votre déploiement utilise une stack d’images Linux, exécutez-la sur Linux. Dans la famille RHEL, cela signifie installer Docker sur Rocky Linux ou AlmaLinux, où Podman apparaît une seconde fois comme le paquet qui fournit déjà la commande docker avant même que vous n’ayez rien installé.
Les jails FreeBSD sont-elles plus sécurisées que les conteneurs Docker ?
Les deux partagent le kernel de l’hôte. Un bug du kernel représente donc un risque pour les deux, et aucun des deux ne constitue la boundary à choisir pour du code réellement non fiable. La différence tient au point de départ. Une jail commence avec un large ensemble d’opérations refusées, que vous réactivez ensuite paramètre par paramètre. Un conteneur Docker démarre avec root dans un ensemble de namespaces, avec certaines capabilities supprimées, et le renforcement supplémentaire est facultatif. En pratique, la configuration compte davantage que le modèle : une jail exécutée avec allow.mount et allow.raw_sockets activés n’est pas plus sûre qu’un conteneur correctement configuré.
Comment sauvegarder une jail ?
Créez un snapshot du dataset, puis envoyez-le. sudo zfs snapshot zroot/jails/containers/web@backup, puis zfs send ce snapshot vers un autre pool ou dans un fichier que vous copiez hors du serveur. Comme une jail conserve tout son userland dans un seul dataset, le snapshot capture les paquets installés et les données à un instant cohérent, ainsi que chaque fichier de configuration que vous avez modifié manuellement. C’est l’inverse de la méthode Docker, où vous sauvegardez les named volumes et le fichier Compose, puis reconstruisez le reste à partir de l’image.
Ai-je besoin de BastilleBSD, ou le système de base suffit-il ?
Le système de base suffit, et c’est le meilleur point de départ. jail.conf, jls, jexec et service jail start couvrent l’ensemble du modèle. Une fois que vous les connaissez, vous pouvez administrer n’importe quel hôte FreeBSD sans devoir apprendre d’abord les outils propres à cet hôte. Bastille est une couche de confort au-dessus de ce système : il initialise les releases, crée des jails légères, applique des templates et écrit pour vous les règles de redirection pf. Apprenez d’abord les commandes de base, puis ajoutez Bastille lorsque le nombre de jails rend la saisie fastidieuse.