FreeBSD Jails VNET Configuration Guide 2026 — Synthetic Context

# freebsd# devops# security# tutorial
FreeBSD Jails VNET Configuration Guide 2026 — Synthetic ContextTedson Myriam

Here's the cleaned-up Markdown version of your HTML article for dev.to: # FreeBSD Jails VNET...

Setting Up FreeBSD 14 VNET Jails with IPv6-Only Networking

I've been running FreeBSD jails for years, but when I discovered VNET jails with proper network isolation, my infrastructure game changed completely. The ability to give each service its own network stack with full firewall control is a game-changer for security and flexibility. Let me walk you through how I set up modern VNET jails with IPv6-only networking on FreeBSD 14.

What You'll Learn

This guide covers setting up FreeBSD 14 VNET jails with:

  • Robust network isolation using epair interfaces and bridge networking
  • IPv6-only architecture for modern networking
  • PF firewall integration at both host and jail levels
  • Practical troubleshooting techniques

FreeBSD Jails offer lightweight virtualization with process and filesystem isolation. While traditional jails share the host's network stack, VNET jails get their own independent network environment. This setup gives you complete control over each jail's networking, including IP addresses, routing tables, and firewalls.

1. Preparing Your Host System

Before creating VNET jails, you need to configure your FreeBSD host properly.

1.1 Kernel Modules and rc.conf Configuration

First, let's load the necessary kernel modules and configure them to load automatically:

# Load modules immediately
kldload if_epair
kldload if_bridge
kldload pf
kldload pflog

# Add to /etc/rc.conf for persistent loading
echo 'if_epair_load="YES"' >> /etc/rc.conf
echo 'if_bridge_load="YES"' >> /etc/rc.conf
echo 'pf_enable="YES"' >> /etc/rc.conf
echo 'pflog_enable="YES"' >> /etc/rc.conf
Enter fullscreen mode Exit fullscreen mode

Next, configure the bridge interface in /etc/rc.conf:

# /etc/rc.conf additions
cloned_interfaces="bridge0"
ifconfig_bridge0="inet6 -ifdisabled up" # IPv6-only bridge
ipv6_enable="YES"
ipv6_gateway_enable="YES"
rtsold_enable="YES" # If host needs to get IPv6 from upstream
rtadvd_enable="YES" # If host acts as a router for jails
Enter fullscreen mode Exit fullscreen mode

1.2 sysctl Tunings

We need to adjust some sysctl parameters for proper networking:

# Enable IP forwarding for IPv4 and IPv6
sysctl net.inet.ip.forwarding=1
sysctl net.inet6.ip6.forwarding=1

# Configure bridge filtering
sysctl net.link.bridge.pfil_member=0
sysctl net.link.bridge.pfil_bridge=1

# Add to /etc/sysctl.conf for persistence
echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf
echo 'net.inet6.ip6.forwarding=1' >> /etc/sysctl.conf
echo 'net.link.bridge.pfil_member=0' >> /etc/sysctl.conf
echo 'net.link.bridge.pfil_bridge=1' >> /etc/sysctl.conf
Enter fullscreen mode Exit fullscreen mode

1.3 Basic Host pf.conf

Here's a basic PF configuration for the host:

# /etc/pf.conf on the host
ext_if = "vtnet0"
bridge_if = "bridge0"
jail_net = "2001:db8:jails::/64"

set skip on lo0
set skip on $bridge_if

scrub in on $ext_if all fragment reassemble
scrub out on $ext_if all fragment reassemble

# Default deny everything
block all

# Allow all outbound traffic from jails
pass out on $ext_if from $jail_net to any keep state

# Allow specific inbound traffic to jails
pass in on $ext_if proto tcp from any to $jail_net port { ssh, http, https } keep state

# Allow host to communicate with jails
pass in quick on $bridge_if from $jail_net to any keep state
pass out quick on $bridge_if from any to $jail_net keep state

# Basic host protection
pass in on $ext_if proto icmp6 from any to any icmp6-type { echoreq, routeradvert, routersol } keep state
pass out on $ext_if proto icmp6 from any to any icmp6-type { echoreq, routeradvert, routersol } keep state
pass in on $ext_if proto tcp from any to ($ext_if) port ssh keep state
Enter fullscreen mode Exit fullscreen mode

After configuring, enable PF:

pfctl -e -f /etc/pf.conf
Enter fullscreen mode Exit fullscreen mode

2. Creating VNET Jails with epair and bridge

Now let's create our first VNET jail with proper network isolation.

2.1 Understanding epair and bridge Interaction

An epair interface is a virtual network device that comes in pairs (epairXa and epairXb). Here's how it works with VNET jails:

  • epairXa goes into the jail's network stack
  • epairXb stays on the host and connects to the bridge
  • The bridge acts as a virtual switch connecting all jail interfaces

2.2 jail.conf Structure for VNET Jails

Here's an example jail.conf entry for an IPv6-only VNET jail:

# /etc/jail.conf
vnet_base {
    path = "/jails/basejail";
    mount.devfs;
    allow.raw_sockets;
    exec.start = "/bin/sh /etc/rc";
    exec.stop = "/bin/sh /etc/rc.shutdown";
}

vnet-jail-01 {
    host.hostname = "vnet-jail-01";
    path = "/jails/vnet-jail-01";
    vnet;
    vnet.interface = "epair0a";

    # Commands executed on the host before jail starts
    exec.prestart += "ifconfig epair0 create";
    exec.prestart += "ifconfig bridge0 addm epair0b";

    # Commands executed inside the jail after it starts
    exec.start += "ifconfig epair0a up";
    exec.start += "ifconfig epair0a inet6 accept_rtadv";
    exec.start += "route add -inet6 default fe80::1%epair0a";

    # Commands executed on the host after jail stops
    exec.poststop += "ifconfig bridge0 deletem epair0b";
    exec.poststop += "ifconfig epair0b destroy";

    mount.devfs;
    allow.raw_sockets;
    exec.clean;
    exec.consolelog = "/var/log/jail_vnet-jail-01_console.log";
    exec.start = "/bin/sh /etc/rc";
    exec.stop = "/bin/sh /etc/rc.shutdown";
}
Enter fullscreen mode Exit fullscreen mode

To start the jail:

jail -c vnet-jail-01
Enter fullscreen mode Exit fullscreen mode

To enter the jail's console:

jexec vnet-jail-01 /bin/csh
Enter fullscreen mode Exit fullscreen mode

3. Configuring IPv6-Only Networking

An IPv6-only architecture simplifies network management and prepares your infrastructure for the future.

3.1 Host rtadvd Configuration

Configure the host to act as a router for the jails:

# /etc/rc.conf (ensure these are present)
ipv6_enable="YES"
ipv6_gateway_enable="YES"
rtadvd_enable="YES"

# /etc/rtadvd.conf
bridge0:\
    :addrs#1:prefix:2001:db8:jails::/64:
Enter fullscreen mode Exit fullscreen mode

Start the rtadvd service:

service rtadvd start
Enter fullscreen mode Exit fullscreen mode

3.2 Jail rc.conf and DNS Configuration

Inside the jail, ensure IPv6 is enabled:

# /jails/vnet-jail-01/etc/rc.conf
ipv6_enable="YES"
rtsol_enable="YES"
Enter fullscreen mode Exit fullscreen mode

Configure DNS in the jail:

# /jails/vnet-jail-01/etc/resolv.conf
nameserver 2001:4860:4860::8888
nameserver 2001:4860:4860::8844
Enter fullscreen mode Exit fullscreen mode

3.3 Static IPv6 Addressing (Alternative)

For services needing stable addresses:

# In /etc/jail.conf for vnet-jail-01
# ...
exec.start += "ifconfig epair0a inet6 2001:db8:jails::10/64 up";
exec.start += "route add -inet6 default 2001:db8:jails::1";
# ...
Enter fullscreen mode Exit fullscreen mode

4. Implementing PF Firewall for Enhanced Security

PF provides powerful firewall capabilities at both host and jail levels.

4.1 Host PF for VNET Jails

The host's PF controls traffic between external networks and the jail bridge:

# /etc/pf.conf (host)
# ...
# Define tags for specific jail services
pass in on $ext_if proto tcp from any to $jail_net port ssh tag SSH_JAIL
pass in on $ext_if proto tcp from any to $jail_net port http tag HTTP_JAIL

# Example: allow SSH to specific jail IP
pass in on $ext_if proto tcp from any to 2001:db8:jails::10 port ssh keep state tag SSH_JAIL_01
Enter fullscreen mode Exit fullscreen mode

4.2 Jail-Specific PF Configuration

Each jail can run its own PF instance:

# /jails/vnet-jail-01/etc/pf.conf
int_if = "epair0a"

set skip on lo0

# Default deny all
block all

# Allow outbound connections
pass out on $int_if all keep state

# Allow inbound SSH
pass in on $int_if proto tcp from any to any port ssh keep state

# Allow ICMP6 for network diagnostics
pass in on $int_if proto icmp6 from any to any icmp6-type { echoreq, echorep, routersol, routeradvert, neighbrsol, neighbradvert } keep state
Enter fullscreen mode Exit fullscreen mode

Enable PF in the jail's /etc/rc.conf:

pf_enable="YES"
pf_rules="/etc/pf.conf"
Enter fullscreen mode Exit fullscreen mode

5. Advanced VNET Features and Troubleshooting

5.1 Multiple Bridges and VLANs

For more complex setups:

# /etc/rc.conf
cloned_interfaces="bridge0 bridge1"
ifconfig_bridge0="inet6 -ifdisabled up"
ifconfig_bridge1="inet6 -ifdisabled up"
Enter fullscreen mode Exit fullscreen mode

5.2 Jail Management Tools

Consider using tools like:

  • CBSD: Powerful framework for managing jails, bhyve, and other virtual environments
  • Pot: Lightweight ZFS-based jail manager focused on immutable infrastructure

5.3 Monitoring and Troubleshooting

Key commands for debugging:

  • ifconfig -a (host and jail)
  • netstat -rn (host and jail)
  • pfctl -sr, pfctl -sa (host and jail)
  • tcpdump -i <interface>
  • dmesg, /var/log/messages, jail console logs

Final Thoughts

Setting up VNET jails with proper network isolation gives you incredible flexibility and security for your FreeBSD infrastructure. The IPv6-only approach simplifies configuration while preparing your systems for the future.

Have you tried setting up VNET jails before? What challenges did you face? I'd love to hear about your experiences in the comments below!


Originally published on synthetic-context.net