SSD Nodes Learn 🎉 VPS dès $5.50/mois
Guides Matt ConnorPar Matt Connor

Jails FreeBSD ou conteneurs Docker : quelles différences ?

Comparez les jails FreeBSD et Docker : userland complet ou images en couches, gestion de l’état, réseau, processus et limites sur un kernel partagé.

Jails FreeBSD et conteneurs Docker, en un paragraphe

Les jails FreeBSD et les conteneurs Docker répondent au même problème, selon deux modèles différents. Ils exécutent tous deux des userlands isolés sur un même kernel partagé ; aucun des deux n’est donc une machine virtuelle. La différence concerne 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 userland 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 pouvez acheter ici. Il s’agit d’une comparaison de deux modèles d’isolation, conçue pour vous permettre de déterminer lequel correspond réellement à une charge de travail et de comprendre la configuration d’une équipe FreeBSD sans devoir la deviner.

Ce qu’est réellement un jail

Les jails sont apparus dans FreeBSD 4.0 en mars 2000. Ils sont donc antérieurs aux cgroups d’environ une décennie à Docker. Le mécanisme repose sur un appel au kernel. jail(8) prend une arborescence de répertoires et démarre des processus à l’intérieur, en leur associant un identifiant de jail. Le kernel refuse ensuite un ensemble défini d’opérations à tout processus portant cet identifiant. Un processus placé dans un jail ne peut pas voir les processus situés en dehors de celui-ci. Il ne peut pas monter ni démonter de systèmes de fichiers, charger de modules du kernel ou se lier à des adresses réseau qui n’ont pas été attribuées au jail. Il n’existe pas de type de namespace distinct à apprendre ni d’activation fonctionnalité par fonctionnalité. Les restrictions s’appliquent en bloc et sont ajustées par les paramètres de la configuration du jail.

Sur l’hôte, jls liste les jails en cours d’exécution et jexec web sh ouvre un shell dans celui nommé web.

Pour créer un jail, placez un userland FreeBSD dans un répertoire. Le système de base s’en charge pour vous :

sudo bsdinstall jail /usr/local/jails/containers/web

Cette commande récupère le jeu de fichiers 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. Décrivez-la 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
jls

jls doit maintenant afficher web avec un JID, son hostname et son adresse IP. Si le 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’un jail exécute le script de boot normal de FreeBSD à l’intérieur de celui-ci. Le jail démarre donc tous les services activés dans son propre /etc/rc.conf. Un conteneur Docker n’a pas d’équivalent, car il exécute le processus entrypoint de l’image et s’arrête lorsque ce processus s’arrête.

Obtenir les logiciels : images et registres, ou userland à remplir

C’est la différence que vous constatez dès le premier jour.

Avec Docker, vous indiquez le 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 associé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 en peu de temps, au lieu 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 pour configurer un serveur nu. 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 réduisent le travail. BastilleBSD est le gestionnaire de jail le plus courant, et c’est un paquet :

sudo pkg install bastille
sudo sysrc bastille_enable=YES
sudo bastille setup
sudo bastille bootstrap 15.1-RELEASE

bastille setup configure le réseau, le stockage et le firewall à votre place. bastille bootstrap télécharge une release une seule fois, puis chaque jail que vous créez réutilise cette release. FreeBSD 15.1 est la release de production actuelle, publiée en juin 2026 ; remplacez-la par la release que vous utilisez.

Créer une jail se fait ensuite avec une commande, puis la remplir en demande 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 web

bastille 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éconstruit.

Le résumé est donc simple. Docker vous fournit les builds d’autres personnes. Les jails vous laissent effectuer vos propres installations. Si le logiciel de votre liste n’est disponible que sous forme d’image de conteneur, la question est déjà tranchée avant que les autres critères puissent intervenir.

État et mises à niveau : ce que ZFS change

Docker sépare volontairement l’état. Le système de fichiers du conteneur est jetable, tandis que vos données résident dans un volume nommé ou un bind mount. 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 comportement prévu 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.

Un jail ne sépare pas l’état, et c’est ZFS qui le permet. L’ensemble du jail constitue un seul dataset :

sudo zfs snapshot zroot/jails/containers/web@pre-upgrade
sudo pkg -j web upgrade
sudo zfs rollback zroot/jails/containers/web@pre-upgrade

Vé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 manuel. Le snapshot est créé en environ une seconde et consomme presque aucun espace tant que le contenu du jail ne change pas. Si la mise à niveau casse le service, le rollback rétablit l’ensemble de l’espace 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 à 2am. Docker ne propose aucun équivalent intégré, car son modèle suppose que vous n’en aurez jamais besoin.

zfs clone constitue l’autre élément. Un clone de snapshot est un nouveau jail accessible en écriture qui partage les blocs inchangés avec son parent. Une copie de staging d’un jail de 3 GB ne consomme donc presque aucun espace disque tant que vous ne la modifiez pas. C’est ainsi qu’un administrateur FreeBSD crée un jail « identique à la production » pour tester une mise à niveau.

La mise à niveau du système de base est distincte de celle des packages. Pour un jail qui contient sa propre copie de l’espace utilisateur :

sudo freebsd-update -b /usr/local/jails/containers/web fetch install

Les jails thin évitent de répéter cette opération. Ils montent une base partagée en lecture seule via nullfs et fournissent à chaque jail sa propre petite couche accessible en écriture. Vous mettez ainsi à jour la base une seule fois, et tous les jails voient le résultat. Bastille crée des jails thin par défaut.

Réseau : ports publiés ou choix du modèle d’adressage

Docker gère le réseau à votre place et vous demande de publier les exceptions. Les conteneurs sont placés sur un bridge, ils 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 ajoute ses propres règles de filtrage des paquets pour obtenir ce comportement. C’est aussi la raison pour laquelle un port de conteneur publié contourne directement ufw.

Avec une 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 la jail à cette adresse. La jail n’a pas sa propre pile réseau. Elle ne peut donc pas exécuter son propre firewall. Elle ne peut pas non plus réellement écouter sur toutes les adresses : un socket de la jail qui demande 0.0.0.0 est réécrit par le kernel avec l’adresse propre à la jail. Deux jails ne peuvent pas toutes les deux écouter sur le port 80 de la même adresse. Vous devez donc attribuer une adresse à chacune ou placer un reverse proxy devant elles.

VNET. Ajoutez vnet; à la 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-la à l’hôte avec un epair, 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 une jail est une règle de redirection pf. Bastille l’encapsule :

sudo bastille rdr web tcp 80 80

Il n’y a ni EXPOSE ni publication automatique. Rien n’atteint une 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 et 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 léger coût à chaque allocation. Ajoutez le tunable dans /boot/loader.conf, puis redémarrez :

kern.racct.enable=1

Définissez ensuite une règle et surveillez-la :

sudo rctl -a jail:web:vmemoryuse:deny=1g
rctl -hu jail:web

rctl -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, plutôt qu’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 et activez le service :

sudo sysrc rctl_enable=YES

C’est sur ce point que Docker est clairement 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 référence un jail défini ailleurs.

Quand la réponse est une machine virtuelle : bhyve

Une jail partage le noyau de l’hôte. Certaines fonctions lui sont donc définitivement inaccessibles. Elle ne peut pas exécuter une autre version du noyau, charger un module du noyau ni exécuter des binaires Linux comme le fait un conteneur Linux. FreeBSD fournit une couche de compatibilité Linux, linuxulator, mais celle-ci n’implémente qu’un sous-ensemble des appels système Linux. Elle ne constitue pas une solution générale pour des images Linux arbitraires.

bhyve est l’hyperviseur de FreeBSD. C’est l’outil adapté lorsque vous avez besoin d’une véritable séparation entre les machines : pour exécuter un autre système d’exploitation, un autre noyau ou un tenant avec lequel vous préférez ne pas partager le noyau. En contrepartie, la mémoire est réservée au lieu d’être partagée, et vous devez maintenir un deuxième noyau à jour. C’est la même décision que sous Linux entre les conteneurs et les machines virtuelles complètes. Elle détermine également si vous avez besoin d’un VPS qui prend en charge la virtualisation imbriquée en dessous.

L’écosystème, la raison concrète 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 la taille de l’écosystème qui l’entoure.

Docker fournit Docker Hub et GHCR, docker compose, Kubernetes lorsqu’un seul serveur 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 tous les projets. Les jails donnent accès à l’arbre de ports FreeBSD, vaste et soigneusement maintenu, ainsi qu’à un ensemble beaucoup plus réduit de bundles d’applications prêts à l’emploi. Lorsqu’un projet publie uniquement une image de conteneur, la solution FreeBSD consiste à consulter sa documentation et à assembler vous-même les différents composants.

Les jails justifient leur choix dans d’autres situations. Utilisez-les si vous exploitez déjà ZFS et accordez de l’importance aux snapshots et au rollback d’un service complet, si vos services sont natifs de FreeBSD, si vous voulez un userland complet par tenant plutôt qu’un processus unique, ou si vous souhaitez 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 décrit FreeBSD comme cohérent. Ce point est davantage développé dans la comparaison plus générale de Linux et FreeBSD comme plateformes serveur et dans les changements apportés par FreeBSD 15 pour les serveurs.

Pour finir, voici une conclusion claire. 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 pouvoir effectuer le rollback de services complets sauvegardés sur ZFS, ou parce que vous utilisez déjà FreeBSD.

FAQ

Puis-je exécuter des images Docker sur FreeBSD ?

Pas les images Linux, et pas dans un cadre pris en charge. 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 en dessous. Il nécessite que fdescfs soit monté sur /dev/fd pour le monitor des conteneurs, ainsi que pf pour le NAT des conteneurs (traduction d’adresses réseau). Les images OCI natives de 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 est composé d’une stack d’images Linux, exécutez-la sur Linux.

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 constitue donc un risque pour les deux, et aucun des deux ne doit être choisi comme frontière pour du code réellement non fiable. La différence tient au point de départ. Une jail commence par refuser un large ensemble d’opérations, que vous réactivez ensuite paramètre par paramètre. Un conteneur Docker commence avec root dans un ensemble de namespaces, avec certaines capabilities supprimées, puis le renforcement supplémentaire est activé au cas par cas. 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 configuré avec soin.

Comment sauvegarder une jail ?

Prenez 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 packages 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 l’habitude Docker, où vous sauvegardez les volumes nommés 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 lire la configuration de n’importe quel hôte FreeBSD sans devoir d’abord apprendre les outils propres à cet hôte. Bastille est une couche de commodité au-dessus de ce système : il amorce 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.