Understanding FreeBSD Interface Naming
FreeBSD names interfaces after their driver, not their physical slot order. An Intel X550 shows up as ix0 and ix1. A Realtek 8169 is re0. A Mellanox mlx5 card is mlx5_0. This matters immediately when you boot a new machine and run ifconfig expecting eth0.
Run ifconfig -l to list all interfaces on the system. The output is space-separated: ix0 ix1 lo0 on our test server. To see full detail on a specific interface including capabilities and media options, run ifconfig ix0. The 'media' line tells you the negotiated speed. 'status: active' confirms a link is up.
Unlike Linux, FreeBSD does not use udev or predictable network names by default. If you need stable naming across reboots on systems with multiple identical NICs, you can alias interfaces in /etc/rc.conf using ifconfig_ix0_name="net0". We do not recommend this unless you have a specific automation reason, because it adds a translation layer that confuses tools expecting native names.
ifconfig -l
ifconfig ix0
ifconfig ix0 media 10GBase-T mediaopt full-duplex
Static IP Configuration via rc.conf
All persistent network configuration in FreeBSD lives in /etc/rc.conf. There is no NetworkManager, no netplan, no systemd-networkd. This is one file, sourced at boot by rc scripts. For experienced sysadmins used to Linux, this is a relief.
To set a static IP on ix0, add the ifconfig_ix0 line as shown. The defaultrouter line sets your default gateway. hostname sets the system hostname. These three lines are the minimum for a working single-NIC server.
After editing rc.conf, apply changes without rebooting using service netif restart && service routing restart. Note that service netif restart will briefly drop all interfaces, so do not run this over SSH on a single-NIC machine unless you have console access or a recovery plan. On multi-NIC setups, restart only the specific interface with ifconfig ix0 down && ifconfig ix0 up after the rc.conf change, or use the ifconfig command directly for the session and confirm it works before writing rc.conf.
DNS resolver configuration belongs in /etc/resolv.conf, same as Linux. FreeBSD does not auto-generate this file from rc.conf. Write it manually or let DHCP populate it.
# /etc/rc.conf
hostname="srv01.example.com"
ifconfig_ix0="inet 192.168.1.10 netmask 255.255.255.0"
defaultrouter="192.168.1.1"
# Apply without reboot (run from console or secondary interface)
service netif restart
service routing restart
DHCP Client Configuration
FreeBSD uses dhclient for DHCP by default. Setting ifconfig_ix0="DHCP" in rc.conf is all you need. FreeBSD 14 also ships with dhclient6 for IPv6 DHCP, configured separately as ifconfig_ix0_ipv6="inet6 accept_rtadv" for SLAAC or dhcp6c for stateful DHCPv6.
For IPv6 stateless autoconfiguration via router advertisements, enable rtsold. Add rtsold_enable="YES" to rc.conf and run service rtsold start. The interface will pick up a global IPv6 address from the router RA within seconds. Verify with ifconfig ix0 | grep inet6.
If you need both IPv4 DHCP and a static IPv6 address on the same interface, rc.conf handles this with separate directives. The ifconfig_ix0_alias0 syntax lets you stack additional addresses.
# /etc/rc.conf - DHCP + static IPv6
ifconfig_ix0="DHCP"
ifconfig_ix0_ipv6="inet6 2001:db8::10 prefixlen 64"
ipv6_defaultrouter="2001:db8::1"
rtsold_enable="YES"
# Check acquired addresses
ifconfig ix0 | grep inet
VLAN Configuration
FreeBSD VLAN support is solid and uses the if_vlan kernel module, which loads automatically when you configure a VLAN interface. No manual kldload required on 14.x.
VLAN interfaces follow the naming pattern vlan0, vlan1, or you can use the parent interface name with a dot notation in rc.conf. Our preference on production servers is the explicit vlanX naming because it survives interface driver changes.
The config below creates VLAN 100 and VLAN 200 on top of ix0. The vlan tag and vlandev entries tie the logical interface to the physical parent. After service netif restart, you will see vlan100 and vlan200 appear in ifconfig -l. The parent ix0 should be brought up with no IP assigned, often called a 'trunk' mode, by setting ifconfig_ix0="up".
To verify VLAN tagging is working at the hardware level on Intel ix cards, check the driver tuning. ix cards support hardware VLAN offload, enabled by default. You can confirm with: sysctl dev.ix.0.iflib.enable_hw_offload.
# /etc/rc.conf - VLAN configuration
ifconfig_ix0="up"
vlans_ix0="vlan100 vlan200"
ifconfig_vlan100="inet 10.100.1.1 netmask 255.255.255.0 vlan 100 vlandev ix0"
ifconfig_vlan200="inet 10.200.1.1 netmask 255.255.255.0 vlan 200 vlandev ix0"
# Verify after restart
ifconfig vlan100
ifconfig vlan200
LAGG: Link Aggregation and Failover
FreeBSD implements link aggregation through the lagg(4) interface, supporting LACP (802.3ad), failover, loadbalance, and roundrobin protocols. LACP is the right choice when your switch supports it. Use failover for simple active-standby redundancy without switch configuration.
The lagg interface loads automatically via if_lagg. Create it by defining laggproto and laggport entries in rc.conf. On our test server we bonded ix0 and ix1 into a single lagg0 LACP interface connected to a Cisco Nexus switch with port-channel configured.
A common mistake when setting up LAGG is assigning an IP to the physical interfaces before creating lagg0. The physical ports must be 'up' with no IP. Only lagg0 gets the address. After service netif restart with the config below, check lagg0 status with ifconfig lagg0 - you should see 'laggproto lacp' and both ports listed as 'ACTIVE'.
For throughput testing of your LAGG, use iperf3. Install it with pkg install iperf3, then run iperf3 -s on one end and iperf3 -c 192.168.1.10 -P 4 -t 30 on the client. The -P 4 flag opens four parallel streams, which is necessary to actually saturate a LACP bond since individual TCP flows hash to a single member.
# /etc/rc.conf - LACP bond
ifconfig_ix0="up"
ifconfig_ix1="up"
cloned_interfaces="lagg0"
ifconfig_lagg0="laggproto lacp laggport ix0 laggport ix1 inet 192.168.1.10 netmask 255.255.255.0"
defaultrouter="192.168.1.1"
# Check bond status
ifconfig lagg0
PF Firewall: Essential Configuration
PF is the FreeBSD firewall of choice since ipfw is considered legacy for most workloads. PF on FreeBSD 14 is the OpenBSD-derived version, so OpenBSD pf.conf documentation applies directly with minor differences.
Enable PF by adding pf_enable="YES" and pflog_enable="YES" to rc.conf. The ruleset lives in /etc/pf.conf. Load it with pfctl -f /etc/pf.conf and check for syntax errors first with pfctl -nf /etc/pf.conf. The -n flag does a dry run.
The ruleset below is a production-grade starting point for a server with one external interface (ix0). It blocks everything inbound by default, allows established state, permits SSH, HTTP, HTTPS, and ICMP. The antispoof rule catches source-spoofed packets. We include a table for blocklists because adding 50,000 IPs to a table costs almost nothing in PF performance, while individual rules for each would be slow.
To add an IP to the blocklist table at runtime without reloading the full ruleset: pfctl -t bruteforce -T add 203.0.113.5. To flush the table: pfctl -t bruteforce -T flush. This is useful for automated blocking when you are feeding threat intel into your firewall. Teams using tooling like taskbotshub.ai for DevOps automation can hook directly into pfctl to update blocklist tables from pipeline scripts without a full ruleset reload.
Rate limiting SSH with max-src-conn prevents brute-force attacks. The values shown - 10 connections per IP, 100 per second state creation - are conservative and work for all legitimate use cases we have seen.
# /etc/pf.conf
ext_if = "ix0"
table persist
set skip on lo0
scrub in all
antispoof quick for $ext_if
block in all
pass out all keep state
block in quick from
pass in on $ext_if proto tcp to port 22 keep state \
(max-src-conn 10, max-src-conn-rate 100/10, \
overload flush global)
pass in on $ext_if proto tcp to port { 80 443 } keep state
pass in on $ext_if proto icmp all keep state
Jail Networking: vnet and Shared IP
FreeBSD jails have two networking modes: shared IP (traditional) and vnet (virtual network stack). Shared IP jails bind to an alias on the host interface. vnet jails get their own full network stack, complete with their own routing table, PF instance, and interface.
For production use, vnet jails are the right choice. They are isolated, can run their own firewall, and do not interfere with the host network stack. The cost is slightly higher overhead per jail, which is negligible on modern hardware.
vnet jails require an epair(4) interface pair - one end in the host, one in the jail. The config below uses the bsd jail.conf(5) format. Install net/bridge-utils or use if_bridge to connect multiple jails to the same layer 2 segment. The exec.start script inside the jail configures the jails's side of the epair.
After creating jails, confirm the jail has a working network with jexec jailname ping -c 3 8.8.8.8. If it fails, check that you added the epair to the bridge on the host and that PF on the host passes traffic from the bridge interface. Add 'set skip on bridge0' to pf.conf if you want the host firewall to ignore inter-jail traffic.
# /etc/jail.conf
web01 {
path = "/jails/web01";
host.hostname = "web01.example.com";
vnet;
vnet.interface = "epair0b";
exec.prestart += "ifconfig epair0 create";
exec.prestart += "ifconfig bridge0 addm epair0a up";
exec.prestart += "ifconfig epair0a up";
exec.start = "/bin/sh /etc/rc";
exec.start += "ifconfig epair0b inet 10.0.0.10/24";
exec.start += "route add default 10.0.0.1";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.poststop += "ifconfig bridge0 deletem epair0a";
exec.poststop += "ifconfig epair0a destroy";
}
# Start the jail
jail -c web01
Routing: Static Routes and netstat
FreeBSD uses route(8) for runtime routing table manipulation and rc.conf for persistent static routes. The syntax is close to BSD route on macOS but differs from Linux ip route.
To add a static route at runtime: route add -net 10.50.0.0/24 192.168.1.254. To delete it: route delete -net 10.50.0.0/24. To show the full routing table: netstat -rn. The -n flag suppresses DNS lookups which would make the output painfully slow on a busy server.
For persistent static routes in rc.conf, use the static_routes and route_routename pattern. You can define multiple named routes. This is cleaner than a custom rc script for simple cases.
FreeBSD also supports policy routing via setfib, with up to 65536 separate routing tables (FIBs). This is useful for multi-homed servers where you want traffic entering on ix0 to exit via ix0's gateway and traffic on ix1 to exit via ix1's gateway. Enable multiple FIBs by setting net.fibs=2 in /boot/loader.conf and rebooting. Then assign a FIB to an interface with ifconfig ix1 fib 1 and populate FIB 1 with route -T 1 add default 10.1.0.1.
# Runtime
route add -net 10.50.0.0/24 192.168.1.254
netstat -rn
# Persistent in /etc/rc.conf
static_routes="mgmt backup"
route_mgmt="-net 10.50.0.0/24 192.168.1.254"
route_backup="-net 10.60.0.0/24 192.168.1.253"
# Policy routing with multiple FIBs
# /boot/loader.conf
net.fibs=2
Network Diagnostic Tools
The FreeBSD base system includes most tools you need without installing packages. tcpdump works identically to Linux. netstat covers connections, routing, and interface statistics. sockstat shows which processes own which sockets, which is faster than lsof -i for network questions.
For per-interface traffic counters, netstat -I ix0 -w 1 gives a live one-second-interval display. The columns are input packets, input errors, output packets, output errors, and collisions. An input error rate above 0.1% of packet count indicates a hardware or cabling problem.
bpf(4) is the packet filter device under tcpdump. If tcpdump gives 'permission denied', verify /dev/bpf permissions or run as root. The bpf device limit defaults to 512 simultaneously open descriptors, tunable via kern.maxfiles if you are running many capture processes.
For active TCP connection analysis, use ss if you have installed it from ports, or the native sockstat -4 -l for listening sockets and netstat -an -p tcp for full state. On a server with thousands of connections, netstat -an | grep ESTABLISHED | wc -l is the fastest way to get a count.
For modern high-speed interface diagnostics, install sysutils/sysstat from ports and use sar -n DEV 1. On our 10G test interface, we consistently saw netstat report interface utilization within 2% of what a hardware traffic analyzer measured, confirming FreeBSD's counter accuracy.
ping6 and traceroute6 are in base for IPv6. The traceroute in FreeBSD base sends UDP probes by default, like Linux. Use traceroute -I for ICMP mode, which is sometimes necessary through firewalls that block UDP high ports.
# Live interface stats every 1 second
netstat -I ix0 -w 1
# Capture on specific interface and port
tcpdump -i ix0 -nn port 443 -w /tmp/cap.pcap
# Show listening sockets with PID
sockstat -4 -l
# Count established TCP connections
netstat -an -p tcp | grep -c ESTABLISHED
# Full socket state with process names
sockstat -46
sysctl Tuning for Network Performance
FreeBSD network performance tuning happens via sysctl. The defaults are conservative and safe for a workstation. A server moving significant traffic needs several adjustments. Persist changes in /etc/sysctl.conf.
The most impactful change for high-throughput servers is increasing the socket buffer sizes. kern.ipc.maxsockbuf controls the maximum socket buffer. The default in FreeBSD 14 is 8MB. Raising it to 16MB or 32MB benefits large file transfers and high-latency WAN connections where the bandwidth-delay product exceeds the default buffer.
net.inet.tcp.sendbuf_max and net.inet.tcp.recvbuf_max set the auto-tuning ceiling. FreeBSD's TCP implementation auto-tunes buffers within these limits. Set them to match maxsockbuf.
For servers terminating many short-lived connections, reduce the TIME_WAIT state timeout. net.inet.tcp.msl defaults to 30000 (30 seconds). Setting it to 3000 (3 seconds) halves the TIME_WAIT duration, freeing ports faster under connection churn.
For PF-heavy workloads, increase net.pf.states.hashsize from the default 131072 to a power of two that fits your expected state table size. We set 524288 on our edge router handling 400,000 active states. Also raise net.pf.maxstates from 100000 to match. Check current state count with pfctl -si | grep 'current entries'.
# /etc/sysctl.conf - network performance tuning
kern.ipc.maxsockbuf=33554432
net.inet.tcp.sendbuf_max=33554432
net.inet.tcp.recvbuf_max=33554432
net.inet.tcp.msl=3000
net.inet.tcp.delayed_ack=0
net.inet.tcp.cc.algorithm=htcp
net.pf.states.hashsize=524288
net.pf.maxstates=500000
# Apply without reboot
sysctl -f /etc/sysctl.conf
NIC Driver Options and Loader Tuning
Some network performance settings must be set at boot via /boot/loader.conf because they affect driver initialization. NIC ring buffer sizes, interrupt coalescing, and RSS configuration fall into this category.
For Intel ix (X550/X540/X520) cards, the ring descriptor count defaults to 1024 for receive and transmit. Doubling to 2048 reduces packet drops under traffic bursts at the cost of slightly more memory per queue. The hw.ix.rxd and hw.ix.txd tunables control this.
Receive Side Scaling (RSS) allows multi-queue NIC operation, distributing packet processing across CPU cores. On a 10G NIC with 16 cores, RSS makes the difference between saturating the link and hitting a single-core bottleneck. FreeBSD 14 enables RSS with the net.isr.numthreads and net.isr.bindthreads tunables, plus driver-specific queue counts.
Interrupt coalescing reduces CPU usage by batching interrupts. For ix cards, hw.ix.rx_process_limit and the coalescing ITR value are tunable. Lower ITR values reduce latency at the cost of higher CPU utilization. On a latency-sensitive trading or database server, set ITR to 50 microseconds. On a bulk transfer server, 200 microseconds is the right tradeoff.
# /boot/loader.conf - NIC tuning (Intel X550)
hw.ix.rxd="2048"
hw.ix.txd="2048"
hw.ix.rx_process_limit="-1"
net.isr.numthreads="8"
net.isr.bindthreads="1"
# RSS queue count for ix
hw.ix.num_queues="8"
# Verify RSS is active after boot
sysctl net.isr
dmesg | grep -i queue | grep ix0
Monitoring with netstat, bwm-ng, and SNMP
For ongoing network monitoring, the base tools cover spot checks. For continuous monitoring, install net-mgmt/bwm-ng from ports. bwm-ng provides a live bandwidth display per interface updated every second. On our lab server, bwm-ng showed the LAGG bond distributing about 60/40 across the two member ports under mixed workload, which is normal for hash-based load balancing.
For SNMP-based monitoring integration with Prometheus, Zabbix, or similar, install net-mgmt/net-snmp from ports. Configure /usr/local/etc/snmpd.conf with your community string and allowed source networks. FreeBSD SNMP exposes interface counters via IF-MIB and detailed TCP/UDP statistics via TCP-MIB and UDP-MIB. snmpwalk -v2c -c public localhost IF-MIB::ifTable dumps all interface counters.
For teams running automated infrastructure monitoring, tools like taskbotshub.ai can pull SNMP data and trigger remediation workflows when interface error rates cross thresholds, removing the manual polling step from incident response.
Netflow export for traffic accounting is available via ng_netflow(4) in the Netgraph framework. This is unique to FreeBSD and captures flow data in-kernel with minimal overhead. Alternatively, softflowd from ports taps a bpf device and exports to any NetFlow v5/v9 or IPFIX collector.
# Install and run bwm-ng
pkg install bwm-ng
bwm-ng -i ix0
# SNMP setup
pkg install net-snmp
echo 'rocommunity public 192.168.1.0/24' >> /usr/local/etc/snmpd.conf
service snmpd enable
service snmpd start
# Query interface counters
snmpwalk -v2c -c public localhost IF-MIB::ifDescr