Important: This article is not a “how-to” for jail and vnet; it is written to give guidance on how you can use /etc/jail.conf and VNET.
I do not claim it is 100% exhaustive, but I hope it will be useful to people who are already familiar with FreeBSD jails and want to use them with VIMAGE.
Useful Links
- freebsd.org
- FreeBSD Handbook: Jails
- FreeBSD Wiki: Network Virtualization
- FreeBSD jail (Wikipedia)
- Google Bulgaria
- man ifconfig
- man jail.conf
- man jail
A Bit of Theory
What is a FreeBSD Jail
Jails enhance the traditional chroot concept in several ways.
With a standard chroot, processes are confined only within the filesystem they can see. All other system resources (users, running processes, the network subsystem) are shared between the chroot and the host OS.
Unlike chroot, a Jail isolates not only the filesystem but also running processes, network resources (VIMAGE/VNET or FIB), and system users.
What is VIMAGE (vnet)
VIMAGE is a kernel module for network virtualization. VIMAGE (or vnet) creates a “private,” more accurately an isolated, network stack.
It requires its own network interface, which can be moved into the Jail—making it visible only inside the jail and not to the host OS.
What is an EPAIR Interface
An epair is one half of a pair of virtual network interfaces connected by a virtual crossover “cable” (for example, epair1a ↔ epair1b).
Sample Topology
Internet
↓
FreeBSD host
↓ natd via rl0
bridge0 ── epair1a ↔ epair1b ── testA.jail.local (lo0, epair1b)
└─ epair2a ↔ epair2b ── testB.jail.local (lo0, epair2b)
└─ epair3a ↔ epair3b ── testC.jail.local (lo0, epair3b)
- The “a” side (e.g. epair1a) is attached tobridge0.
- The “b” side (e.g. epair1b) is placed inside the Jail.
- Traffic flows: Jail ↔ epair1b↔epair1a→bridge0→ Internet (via NAT).
Note: With Jail+vnet the network stack is isolated. You can create other useful interfaces (loopback, VLANs, etc.), but the kernel still sees all interfaces, which imposes some limitations.
Configuration
1. FreeBSD Kernel
When recompiling the kernel, enable:
options         VIMAGE
2. Jail Environment
I prefer a separate ZFS dataset for each jail, for example:
/jails/testjail
You can also use a skeleton template or ezjail.
3. Jail World
After buildworld, install into your jail directory:
cd /usr/src
make installworld   DESTDIR=/jails/testjail
make distribution   DESTDIR=/jails/testjail
- installworldcopies the binaries.
- distributioncopies the base configuration files.
Main Configuration Files
/etc/rc.conf
cloned_interfaces="bridge0"
firewall_enable="YES"
firewall_script="/etc/ipfw.conf"
natd_enable="YES"
natd_flags="-f /etc/natd.conf"
jail2_enable="YES"
jail2_list="testjail"
/etc/ipfw.conf
#!/bin/sh
fwcmd="/sbin/ipfw -q"
eif="rl0"
${fwcmd} -f flush
# NAT rule
${fwcmd} add 65532 divert natd ip from any to any via ${eif}
/etc/natd.conf
interface     rl0
dynamic       yes
same_ports    yes
# Redirect port 80 to 10.10.15.100:80
redirect_port tcp 10.10.15.100:80 192.168.0.10:80
/etc/jail.conf
# --- Default Settings ---
vnet;                             # Enable VIMAGE/VNET
mount.devfs;                      # Mount /dev in each jail
exec.start = "ifconfig lo0 127.0.0.1/8";
exec.stop  = "/bin/sh /etc/rc.shutdown";
# --- Jail Settings ---
testjail {
    path            = /jails/testjail;
    host.hostname   = testjail.jail.local;
    jid             = 1;
    mount.fstab     = /etc/fstab.testjail.local;
    devfs_ruleset   = 5;
    exec.prestart  = "ifconfig epair1 create";
    exec.prestart += "ifconfig bridge0 addm epair1a up";
    exec.prestart += "ifconfig bridge0 alias 10.10.15.1/24";
    exec.prestart += "ifconfig epair1a up";
    vnet.interface = "epair1b";
    exec.start     = "ifconfig epair1b 10.10.15.100/24";
    exec.start    += "route add default 10.10.15.1";
    exec.start    += "/bin/sh /etc/rc";
    exec.poststop  = "ifconfig bridge0 deletem epair1a";
    exec.poststop += "ifconfig bridge0 -alias 10.10.15.1";
    exec.poststop += "ifconfig epair1a destroy";
}
Tip: You can use almost any variable from
sysctl -a | grep jailinside/etc/jail.conf.
Understanding /etc/jail.conf
- Anything outside of { }applies as default to all jails.
- Anything inside { }is specific to that named jail.
- You can have unlimited named blocks, one per jail.
Example Structure
# Default settings
default_setting_1;
default_setting_2;
jail1 {
    # local settings...
}
jail2 {
    # local settings...
}
Key Variables
- exec.prestart— runs before the jail starts
- exec.start— runs when the jail starts
- exec.poststop— runs after the jail stops
The first assignment uses =, and subsequent additions use +=, whether in the default section or inside a jail block.
Starting and Stopping
- 
    Start: jail -c testjail
- 
    Stop: jail -r testjail