FreeBSD.software
Home/Guides/Bastille vs iocage: FreeBSD Jail Manager Comparison
comparison·2026-04-09·12 min read

Bastille vs iocage: FreeBSD Jail Manager Comparison

Bastille vs iocage for FreeBSD jail management: features, templates, ZFS integration, networking, active development status, and which jail manager to choose.

Bastille vs iocage: FreeBSD Jail Manager Comparison

FreeBSD jails are one of the most powerful isolation features in any operating system, but managing them by hand -- creating datasets, configuring networking, maintaining base systems, applying updates -- gets tedious fast. Jail managers automate this. Bastille and iocage are the two most popular options, and they take meaningfully different approaches to the same problem.

Bastille is the newer project, actively maintained, template-driven, and designed with a DevOps mindset. Iocage is older, feature-rich, and was for years the default recommendation. But iocage's development has slowed considerably, and the community has shifted toward Bastille.

This comparison covers both tools in depth so you can make an informed choice.

TL;DR -- Quick Verdict

Choose Bastille if you want active development, a template system for repeatable jail configurations, good documentation, a responsive maintainer, and a tool that is gaining community momentum. Bastille is the safer long-term bet.

Choose iocage if you need features that Bastille lacks (like VNET thin jails or some advanced ZFS snapshot workflows), you already have an iocage-managed infrastructure, or your workflow depends on iocage-specific behavior. But be aware that iocage's development pace has slowed significantly.

Project Status and Maintenance

This matters more than features. A jail manager that stops receiving updates eventually breaks when FreeBSD changes.

Bastille

Bastille is maintained by Christer Edwards and has an active contributor community. As of early 2026, Bastille receives regular commits, responds to issues promptly, and tracks FreeBSD releases. The project has clear documentation and a growing ecosystem of community templates.

Bastille is written in shell script (POSIX sh), which makes it easy to understand, debug, and contribute to. Every command is auditable -- you can read exactly what Bastille does when you run any operation.

Iocage

Iocage has a complicated history. The original iocage was a shell script by James O'Gorman. It was rewritten in Python (iocage / py-iocage) by Brandon Schneider. The Python version became the standard, but its development has slowed. Commits are infrequent, issues accumulate, and FreeBSD version support sometimes lags.

Iocage is not abandoned -- it still works and is in the FreeBSD ports tree. But the gap between Bastille's development velocity and iocage's is growing.

Installation

Bastille

sh
pkg install bastille sysrc bastille_enable="YES"

Bootstrap a FreeBSD release for jail use:

sh
bastille bootstrap 14.2-RELEASE update

This downloads the FreeBSD base system and stores it on ZFS. The update flag applies the latest security patches automatically.

Iocage

sh
pkg install py311-iocage sysrc iocage_enable="YES"

Activate iocage on a ZFS pool:

sh
iocage activate zroot iocage fetch --release 14.2-RELEASE

Both tools require ZFS. Neither works well without it -- ZFS clones and snapshots are fundamental to how both tools create and manage jails.

Creating Jails

Bastille

sh
bastille create webserver 14.2-RELEASE 10.0.0.10

This creates a thick jail named webserver running FreeBSD 14.2, with IP address 10.0.0.10 on the default interface. The jail starts automatically if bastille_enable is set.

For a VNET jail with its own network stack:

sh
bastille create -V webserver 14.2-RELEASE 10.0.0.10/24

Start and stop jails:

sh
bastille start webserver bastille stop webserver bastille restart webserver bastille list

Iocage

sh
iocage create -n webserver -r 14.2-RELEASE ip4_addr="em0|10.0.0.10/24"

For a VNET jail:

sh
iocage create -n webserver -r 14.2-RELEASE vnet=on \ ip4_addr="vnet0|10.0.0.10/24" \ defaultrouter=10.0.0.1

Start and stop:

sh
iocage start webserver iocage stop webserver iocage restart webserver iocage list

The syntax differs but the result is similar. Iocage's property-based configuration (key=value) is more verbose but also more explicit about what each setting does.

Templates and Automation

This is where Bastille has a clear advantage.

Bastille Templates

Bastille templates are declarative configuration files that define everything a jail needs. They look like Dockerfiles for jails:

sh
bastille create webserver 14.2-RELEASE 10.0.0.10 bastille template webserver cedwards/nginx

A Bastille template (Bastillefile) looks like this:

shell
ARG DOMAIN=example.com PKG nginx PKG openssl SYSRC nginx_enable=YES CP usr/local/etc/nginx/nginx.conf CMD nginx -t SERVICE nginx start FSTAB /usr/local/www/mysite usr/local/www/mysite nullfs rw 0 0

Templates can be stored in Git repositories and applied with a URL:

sh
bastille template webserver https://gitlab.com/bastillebsd-templates/nginx

The template system supports:

  • PKG -- Install packages
  • SYSRC -- Set rc.conf values
  • CP -- Copy files into the jail
  • CMD -- Run commands inside the jail
  • SERVICE -- Start services
  • FSTAB -- Mount host directories into the jail
  • RDR -- Set up port redirections (with pf)
  • ARG -- Template variables
  • INCLUDE -- Compose templates from other templates
  • LIMITS -- Set resource limits (rctl)
  • CONFIG -- Apply sysctl settings

This is a complete infrastructure-as-code system for jails. You can version-control your templates, share them, and reproduce environments reliably.

Iocage Templates

Iocage has a template concept, but it works differently. An iocage template is a stopped jail that is cloned to create new jails:

sh
# Create a jail and configure it iocage create -n nginx-template -r 14.2-RELEASE iocage exec nginx-template "pkg install -y nginx" # Convert to template iocage stop nginx-template iocage set template=yes nginx-template # Create jails from template iocage create -n web1 -t nginx-template ip4_addr="em0|10.0.0.11/24" iocage create -n web2 -t nginx-template ip4_addr="em0|10.0.0.12/24"

This is ZFS-clone-based templating -- fast and space-efficient (clones share blocks with the template). But it is not declarative. You cannot look at a file and know what the template contains. You cannot version-control it in Git. You cannot compose templates.

ZFS Integration

Both tools are built on ZFS, but they use it differently.

Bastille ZFS

Bastille stores jails under a configurable ZFS dataset. The default layout:

shell
zroot/bastille/jails/webserver # Jail root zroot/bastille/releases/14.2-RELEASE # Base release zroot/bastille/templates # Downloaded templates zroot/bastille/backups # Exported jails

Bastille supports thick jails (full copy of the base system) and thin jails (nullfs mounts from the release). Thick jails use more disk but are independent of the release. Thin jails save disk but share the base system read-only.

Export and import jails:

sh
bastille export webserver # Creates /usr/local/bastille/backups/webserver-*.xz bastille import /path/to/webserver-*.xz

ZFS snapshots are used for exports but are not directly exposed as a jail management feature (you use zfs snapshot directly).

Iocage ZFS

Iocage has deeper ZFS integration. Its snapshot and clone management is a first-class feature:

sh
# Snapshot a jail iocage snapshot webserver # List snapshots iocage snaplist webserver # Rollback to a snapshot iocage rollback webserver@ioc-2026-04-09 # Clone a jail from a snapshot iocage clone webserver@ioc-2026-04-09 -n webserver-test

Iocage also uses ZFS clones for its template system, making jail creation from templates nearly instantaneous.

The ZFS-native snapshot management is one area where iocage genuinely excels over Bastille.

Networking

Bastille Networking

Bastille supports both shared-IP and VNET networking.

Shared IP (aliases on host interface):

sh
bastille create web 14.2-RELEASE 10.0.0.10

VNET (dedicated virtual network stack per jail):

sh
bastille create -V web 14.2-RELEASE 10.0.0.10/24

Bastille integrates with pf for port redirection:

sh
bastille rdr web tcp 80 80 bastille rdr web tcp 443 443

This adds pf rdr rules automatically. The generated rules go into /usr/local/etc/bastille/pf.conf which you include from your main pf.conf.

Iocage Networking

Iocage also supports shared-IP and VNET:

sh
# Shared IP iocage create -n web ip4_addr="em0|10.0.0.10/24" # VNET iocage create -n web vnet=on ip4_addr="vnet0|10.0.0.10/24" \ defaultrouter=10.0.0.1

VNET in iocage supports bridge configuration:

sh
iocage create -n web vnet=on \ ip4_addr="vnet0|10.0.0.10/24" \ vnet_default_interface=em0 \ defaultrouter=10.0.0.1

Both tools handle VNET competently. The configuration syntax differs but the underlying FreeBSD networking is identical.

Package Management Inside Jails

Bastille

sh
bastille pkg webserver install nginx postgresql16-server bastille pkg webserver upgrade bastille pkg ALL upgrade # Upgrade packages in all jails

The ALL target is powerful -- it applies operations to every jail simultaneously.

Iocage

sh
iocage exec webserver "pkg install -y nginx postgresql16-server" iocage exec webserver "pkg upgrade -y"

Iocage does not have a dedicated package subcommand -- you use exec to run pkg inside the jail. This works fine but is less ergonomic than Bastille's approach.

Resource Limits

Bastille

Bastille supports rctl resource limits:

sh
bastille limits webserver memoryuse:deny=2G bastille limits webserver maxproc:deny=200 bastille limits webserver cputime:devctl=3600

Limits can also be set in templates:

shell
LIMITS memoryuse:deny=2G LIMITS maxproc:deny=200

Iocage

Iocage exposes resource limits as jail properties:

sh
iocage set memoryuse=2G:deny webserver iocage set maxproc=200:deny webserver

Both tools use the same underlying rctl framework. The interface differs but the result is identical.

Updating Jails

Bastille

Update the base release, then apply to all jails:

sh
bastille update 14.2-RELEASE bastille upgrade 14.2-RELEASE 14.3-RELEASE # Major version upgrade

For thin jails, updating the release updates all jails sharing it. For thick jails, each jail must be updated individually.

Iocage

sh
iocage update webserver iocage upgrade webserver -r 14.3-RELEASE

Iocage updates jails individually, which is more granular but slower when you have many jails.

Command Comparison

| Operation | Bastille | Iocage |

|---|---|---|

| Create jail | bastille create name rel ip | iocage create -n name -r rel ip4_addr=... |

| List jails | bastille list | iocage list |

| Start jail | bastille start name | iocage start name |

| Enter console | bastille console name | iocage console name |

| Run command | bastille cmd name command | iocage exec name command |

| Install package | bastille pkg name install pkg | iocage exec name "pkg install pkg" |

| Destroy jail | bastille destroy name | iocage destroy name |

| Export jail | bastille export name | iocage export name |

| Apply template | bastille template name tpl | Manual or clone-based |

| Port redirect | bastille rdr name tcp 80 80 | Manual pf rules |

| Resource limits | bastille limits name ... | iocage set ... name |

| Operate on all | bastille cmd ALL ... | No equivalent |

Community and Ecosystem

Bastille

  • Active maintainer and community
  • Growing template library (BastilleBSD-Templates on GitLab)
  • Regular conference talks and documentation updates
  • Shell-script codebase invites contributions
  • Integration with Ansible via community roles

Iocage

  • Slower development but still maintained
  • Larger existing user base (legacy installations)
  • Python codebase is harder for casual contributors
  • TrueNAS CORE used iocage (but TrueNAS SCALE moved to Linux)
  • More Stack Overflow answers and forum posts (historical)

Performance

Both tools have negligible management overhead. The jails themselves perform identically regardless of which tool manages them -- the jail is a kernel feature, not a property of the management tool.

The only measurable difference is in jail creation time:

| Operation | Bastille | Iocage |

|---|---|---|

| Create thick jail | ~30s | ~30s |

| Create thin jail | ~2s | ~3s |

| Create from template | N/A (apply template ~5-30s) | ~2s (ZFS clone) |

| Start jail | <1s | <1s |

| Destroy jail | <1s | <1s |

Iocage's ZFS-clone-based templating is faster for jail creation, but Bastille's template application is more flexible (it can modify an existing jail rather than only creating from scratch).

Migration Between Tools

Iocage to Bastille

There is no automated migration tool. The process:

sh
# Export from iocage iocage export webserver # Note the jail's configuration iocage get all webserver > webserver-config.txt # Create in Bastille bastille create webserver 14.2-RELEASE 10.0.0.10 # Copy data from the iocage export into the Bastille jail tar xf /iocage/images/webserver_*.zip -C /usr/local/bastille/jails/webserver/root/

In practice, it is cleaner to recreate jails from scratch using Bastille templates -- which is itself a good reason to adopt templates.

Bastille to Iocage

sh
bastille export webserver # Extract and import manually into iocage

Neither direction is seamless. Plan for recreation rather than migration.

FAQ

Is iocage abandoned?

Not formally abandoned, but development has slowed dramatically. The Python rewrite still works on current FreeBSD releases, but bug fixes and new features arrive slowly. If you are starting fresh, Bastille is the better investment.

Can I run both Bastille and iocage on the same system?

Technically yes, but do not do it. Both tools manage ZFS datasets and jail configurations, and having two tools manipulate the same system invites conflicts. Choose one and use it exclusively.

Do Bastille templates work like Dockerfiles?

They are inspired by Dockerfiles but operate differently. Docker builds layers; Bastille applies commands to an existing jail. There is no layer caching or image registry. Templates are more like Ansible playbooks than Docker builds -- they describe desired state and apply it.

Which tool does TrueNAS use?

TrueNAS CORE (FreeBSD-based) used iocage for jail management. TrueNAS SCALE moved to Linux and Docker. The future TrueNAS direction does not depend on either Bastille or iocage, so this should not influence your choice.

Can I use Bastille with Ansible?

Yes. Bastille jails are standard FreeBSD jails. Ansible can connect to them via SSH (if sshd runs inside the jail) or via the jail connection plugin. There are also community Ansible roles that wrap Bastille commands for jail lifecycle management.

How does Bastille handle jail dependencies?

Bastille does not have built-in dependency management between jails. If jail B depends on jail A, you manage startup order through rc.conf or a wrapper script. Iocage has a priority property for boot ordering, which is slightly more structured.

Which tool is better for large-scale deployments (50+ jails)?

Bastille's ALL target and template system make it better suited for managing many jails. Updating, patching, and configuring dozens of jails is straightforward with Bastille templates. Iocage requires more manual orchestration at scale, though scripting around iocage list and iocage exec is possible.

Can either tool manage jails remotely?

Neither Bastille nor iocage has built-in remote management. Both are local tools. For remote management, use Ansible, SSH, or CBSD (which has its own remote node management).

Get more FreeBSD guides

Weekly tutorials, security advisories, and package updates. No spam.