iocage vs Bastille vs pot: FreeBSD Jail Managers Compared
FreeBSD jails are powerful. Managing them by hand is not. Once you move beyond two or three static jails, you need a tool that handles creation, networking, updates, templates, and lifecycle management without requiring you to write custom scripts for everything.
Three jail managers dominate the FreeBSD landscape in 2026: Bastille, iocage, and pot. Each takes a different approach to the same problem. This guide compares all three with real commands, real configurations, and practical recommendations.
For jail fundamentals, see our FreeBSD jails guide. For a broader tool comparison including cbsd and AppJail, read best jail manager for FreeBSD.
TL;DR -- Quick Verdict
Choose Bastille for most deployments. It has the best balance of simplicity, features, active development, and community support. If you manage 1-100 jails on one or a few hosts and want clean syntax with sensible defaults, start here.
Choose pot if you need Nomad orchestration. pot was designed as a Nomad task driver and is the only jail manager with first-class multi-host scheduling. If you are building a distributed jail infrastructure managed by HashiCorp Nomad, pot is the right tool.
Choose iocage only if you have an existing iocage deployment that works well. iocage's development has slowed significantly, and new installations should evaluate Bastille or pot first.
Overview
| | Bastille | iocage | pot |
|---|---|---|---|
| Language | POSIX sh | Python | POSIX sh |
| Dependencies | Base system only | Python 3 | Base system only |
| ZFS required | No (works with UFS too) | Yes | Yes |
| Active development | Very active | Slow | Active |
| Networking | Shared IP, VNET, loopback | Shared IP, VNET | Shared IP, VNET, alias |
| Templates | Bastillefile | No (manual scripting) | Flavours |
| Nomad integration | No | No | Yes (native task driver) |
| Thick/thin jails | Both | Both | Thick only |
| Release bootstrap | Yes | Yes | Yes |
| Latest version (early 2026) | 0.10.x | 1.8 | 0.17.x |
| License | BSD | BSD | BSD |
Bastille
Bastille is a shell-based jail manager that emphasizes simplicity, Docker-inspired workflows, and zero external dependencies. It is written entirely in POSIX sh and requires nothing beyond the FreeBSD base system.
Install
shpkg install bastille sysrc bastille_enable="YES"
Bootstrap and Create
sh# Download and patch a FreeBSD release bastille bootstrap 14.2-RELEASE update # Create a jail bastille create webserver 14.2-RELEASE 10.0.0.1 # Start it bastille start webserver # Enter the jail bastille console webserver
Networking
Bastille supports three networking modes:
Shared IP (default):
shbastille create myjail 14.2-RELEASE 10.0.0.1
The jail shares the host's network stack and binds to the specified IP address (which must be configured as an alias on a host interface).
VNET (full network stack isolation):
shbastille create myjail 14.2-RELEASE 10.0.0.1 -V
Each jail gets its own network stack with its own routing table, firewall state, and interfaces. Requires a bridge interface on the host.
Loopback:
shbastille create myjail 14.2-RELEASE 127.0.0.1
Jail only accessible from the host. Useful for backend services.
Bastillefile (Templates)
Bastille's most powerful feature is the Bastillefile -- a template system inspired by Dockerfiles that automates jail provisioning.
shell# Bastillefile for a web server PKG nginx SYSRC nginx_enable=YES CP /usr/local/etc/nginx/nginx.conf usr/local/etc/nginx/nginx.conf SERVICE nginx start CMD nginx -t
Apply a template:
shbastille template webserver myuser/web-template
Templates can be stored in Git repositories and applied to any jail. This provides reproducible, version-controlled jail provisioning -- the closest thing to Dockerfiles in the FreeBSD world.
Updates
sh# Update the base release bastille bootstrap 14.2-RELEASE update # Update all jails bastille update 14.2-RELEASE # Upgrade packages in a jail bastille pkg ALL upgrade -y
Strengths
- Zero dependencies beyond base FreeBSD
- Clean, consistent CLI syntax
- Bastillefile templates for reproducible provisioning
- Active development with responsive maintainer
- Good documentation and growing community
- Works with both ZFS and UFS
Weaknesses
- No multi-host management (single-host only)
- No Nomad/orchestrator integration
- VNET setup requires manual bridge configuration
- No web UI
iocage
iocage was historically the most popular jail manager on FreeBSD. Written in Python, it provides rich ZFS integration and a comprehensive feature set. However, development has slowed significantly since 2022, and the project's future is uncertain.
Install
shpkg install py311-iocage sysrc iocage_enable="YES"
Activate and Create
sh# Activate iocage on a ZFS pool iocage activate zroot # Fetch a release iocage fetch -r 14.2-RELEASE # Create a jail iocage create -n webserver -r 14.2-RELEASE ip4_addr="em0|10.0.0.1/24" # Start it iocage start webserver # Enter the jail iocage console webserver
Networking
iocage supports shared IP and VNET:
Shared IP:
shiocage create -n myjail -r 14.2-RELEASE ip4_addr="em0|10.0.0.1/24"
VNET:
shiocage create -n myjail -r 14.2-RELEASE vnet=on ip4_addr="vnet0|10.0.0.1/24" \ defaultrouter="10.0.0.254"
iocage's VNET support is well-implemented and was one of its early differentiators.
Jail Properties
iocage uses a property-based configuration system:
sh# Set properties iocage set boot=on webserver iocage set allow_raw_sockets=1 webserver iocage set memoryuse=8G:deny webserver # List all properties iocage get all webserver
Snapshots and Cloning
sh# Snapshot iocage snapshot webserver # List snapshots iocage snaplist webserver # Clone a jail iocage clone webserver -n webserver-staging
Updates
sh# Update base jail to latest patchlevel iocage update webserver # Upgrade to new release iocage upgrade -r 14.2-RELEASE webserver
Strengths
- Rich ZFS integration (snapshots, clones, properties)
- Well-tested VNET implementation
- Property-based configuration is intuitive
- Good snapshot and clone management
- Extensive documentation (historical)
Weaknesses
- Development has stalled: Few commits in 2024-2025. Multiple forks exist but none has achieved dominance.
- Python dependency: Requires Python 3 runtime, which adds complexity and potential breakage on upgrades.
- No template system: No equivalent to Bastillefiles. Jail provisioning must be scripted externally.
- Activation issues: The
iocage activatestep and ZFS pool binding can be confusing. - Bug fixes slow: Known issues may not be resolved promptly.
pot
pot is a jail manager designed with one unique goal: integration with HashiCorp Nomad for multi-host jail orchestration. Written in POSIX sh, it requires only the base system plus ZFS.
Install
shpkg install pot sysrc pot_enable="YES" # Initialize pot init
Create and Configure
sh# Create a pot (jail) pot create -p webserver -t single -b 14.2 # Add a network interface pot set-attr -p webserver -A network-type -V alias # Set resource limits pot set-rss -p webserver -M 2G # Start pot start webserver # Enter pot term webserver
Flavours (Templates)
pot uses "flavours" for jail provisioning -- shell scripts that run inside the jail at creation time:
sh# Create a flavour mkdir -p /usr/local/etc/pot/flavours cat > /usr/local/etc/pot/flavours/web.sh << 'EOF' #!/bin/sh pkg install -y nginx sysrc nginx_enable="YES" service nginx start EOF chmod +x /usr/local/etc/pot/flavours/web.sh # Create a jail with the flavour pot create -p webserver -t single -b 14.2 -f web
Nomad Integration
pot's distinguishing feature is its Nomad task driver. This allows HashiCorp Nomad to schedule and manage FreeBSD jails across a cluster of machines, just as it manages Docker containers on Linux.
hcl# Nomad job file job "webserver" { datacenters = ["dc1"] group "web" { task "nginx" { driver = "pot" config { image = "https://pot-registry.example.com/nginx_14_2" pot = "nginx" tag = "1.0" command = "/usr/sbin/nginx" network_mode = "alias" } resources { cpu = 500 memory = 256 } } } }
Install the Nomad pot driver:
shpkg install nomad-pot-driver
This enables:
- Multi-host jail scheduling
- Service discovery (Consul integration)
- Health checks
- Rolling updates
- Auto-scaling
- Resource allocation across a cluster
No other jail manager provides this capability.
Image Registry
pot supports a simple image distribution mechanism:
sh# Export a pot as an image pot export -p webserver # Import on another host pot import -p webserver -U https://registry.example.com/webserver.xz
This is analogous to Docker image push/pull, though simpler.
Strengths
- Nomad integration: Unique, powerful, production-tested.
- No external dependencies: Pure sh, base system only.
- Image export/import: Simple image distribution.
- Flavours: Straightforward provisioning templates.
- Active development: Regular releases, responsive maintainer.
Weaknesses
- ZFS required: Cannot use UFS.
- Less polished CLI: Commands are functional but less intuitive than Bastille.
- Smaller community: Fewer users, less community documentation.
- Nomad learning curve: The primary value proposition requires Nomad expertise.
- No VNET in all modes: Networking options are more limited than Bastille or iocage.
Feature Comparison
| Feature | Bastille | iocage | pot |
|---|---|---|---|
| ZFS snapshots | Yes (if ZFS) | Yes | Yes |
| ZFS clones | Yes (if ZFS) | Yes | Yes |
| UFS support | Yes | No | No |
| VNET networking | Yes | Yes | Limited |
| Shared IP | Yes | Yes | Yes |
| NAT/redirect | pf rules (manual) | Manual | Built-in alias mode |
| Templates | Bastillefile | No | Flavours |
| Image export/import | No (use ZFS send) | No (use ZFS send) | Yes (native) |
| Multi-host scheduling | No | No | Yes (Nomad) |
| Resource limits (RCTL) | Manual | Yes (properties) | Yes (set-rss) |
| Bulk jail management | bastille cmd ALL | iocage exec ALL | Manual or Nomad |
| Boot on startup | Yes | Yes | Yes |
| Thick jails | Yes | Yes | Yes |
| Thin jails (shared base) | Yes | Yes | No |
| Web UI | No | No | No |
| IPv6 | Yes | Yes | Yes |
| Jail rename | Yes | No | No |
| Automatic updates | bastille update | iocage update | Manual |
| Log management | Standard syslog | Standard syslog | Standard syslog |
Networking
All three support shared IP (jail binds to a host IP alias). Bastille and iocage also support VNET (full network stack per jail with epair interfaces bridged to the host). For NAT, configure pf on the host -- this works identically with all three managers.
VNET example (Bastille):
shsysrc cloned_interfaces="bridge0" sysrc ifconfig_bridge0="inet 10.0.0.254/24 addm em0 up" bastille create web 14.2-RELEASE 10.0.0.1 -V
VNET example (iocage):
shiocage create -n web -r 14.2-RELEASE vnet=on \ ip4_addr="vnet0|10.0.0.1/24" \ defaultrouter="10.0.0.254"
Performance Impact
Jail managers add negligible runtime overhead -- jails are a kernel feature, and the manager only handles lifecycle operations. However, creation and startup times differ:
| Operation | Bastille | iocage | pot |
|---|---|---|---|
| Create jail (ZFS) | ~3s | ~8s | ~5s |
| Create jail (UFS) | ~10s | N/A | N/A |
| Start jail | ~1s | ~3s | ~2s |
| Stop jail | ~1s | ~2s | ~1s |
| Apply template | ~10-30s | N/A | ~10-30s |
| Snapshot | ~0.5s | ~1s | ~0.5s |
iocage is slower because Python startup adds overhead to every command. Bastille and pot are both shell-based and start instantly.
For running workloads, there is zero performance difference between the three -- the jail is a kernel construct that performs identically regardless of which tool created it.
Real-World Deployment Patterns
Pattern 1: Small Hosting Provider (Bastille)
10-50 jails on a single server. Each client gets a jail with VNET networking. Bastillefiles define standard configurations (web server, database, etc.).
shbastille create client1-web 14.2-RELEASE 10.0.1.1 -V bastille template client1-web hosting/web-stack bastille create client1-db 14.2-RELEASE 10.0.1.2 -V bastille template client1-db hosting/database-stack
Pattern 2: Distributed Service Mesh (pot + Nomad)
20 servers running 200 jails. Services scheduled by Nomad, discovered through Consul, load-balanced automatically. This pattern is unique to pot.
Pattern 3: CI/CD Build Jails (Bastille)
Build servers create temporary jails for each CI job, run tests, and destroy the jail.
shJAIL_NAME="ci-${BUILD_ID}" bastille create $JAIL_NAME 14.2-RELEASE 10.0.10.${BUILD_NUMBER} bastille template $JAIL_NAME ci/build-environment bastille cmd $JAIL_NAME make test EXIT_CODE=$? bastille destroy $JAIL_NAME exit $EXIT_CODE
FAQ
Which jail manager should I use for a single web server jail?
For a single jail, you do not need a jail manager at all. Write a jail.conf entry and use the base system's jail command. If you want a manager for consistency, Bastille is the simplest choice.
Is iocage dead?
Not dead, but development is slow. The Python codebase works and existing deployments are fine. However, bug fixes and new features are infrequent. If you are starting a new project, Bastille or pot are better choices with more active development.
Can I mix jail managers on the same host?
Technically possible but not recommended. Each manager has its own directory structure, ZFS dataset layout, and network configuration assumptions. Running two managers on the same host creates confusion and potential conflicts. Pick one and stick with it.
Does Bastille support thin jails?
Yes. Bastille can create thin jails that share a read-only base with other jails, reducing disk usage. Use bastille create -T or configure thin jails in the Bastille configuration. Thick jails (full copy of base) are the default.
How does pot compare to Docker?
pot + Nomad provides a similar workflow to Docker + Kubernetes: image-based deployment, scheduling, service discovery, health checks. The key differences: pot jails are FreeBSD-only, the image ecosystem is tiny compared to Docker Hub, and the tooling is less polished. The advantage: jails are lighter than containers, ZFS integration is native, and the security model is simpler.
Can any of these managers run Linux applications in jails?
No. FreeBSD jails run FreeBSD userland. The Linux compatibility layer (Linuxulator) can run Linux binaries within a jail, but the jail itself is a FreeBSD environment. None of the jail managers provide Linux container functionality.
What about cbsd and AppJail?
cbsd is the most feature-rich jail manager (also manages bhyve VMs and Xen guests) but has the steepest learning curve. AppJail is newer, with a Makejail build system inspired by Dockerfiles. Both are worth evaluating. See our best jail manager for FreeBSD guide for coverage of all five active tools.
How do I handle persistent storage across jail recreations?
All three managers support ZFS datasets that persist independently of the jail. Mount external datasets into jails:
sh# Bastille bastille mount webserver /storage/data usr/local/data nullfs rw 0 0 # iocage iocage fstab -a webserver /storage/data /usr/local/data nullfs rw 0 0 # pot pot mount-in -p webserver -d /storage/data -m /usr/local/data
This ensures data survives jail destruction and recreation.