How to Tune FreeBSD Kernel Parameters with sysctl
FreeBSD exposes hundreds of kernel parameters through sysctl that control network behavior, memory management, security policies, file descriptor limits, and hardware interaction. The default values are conservative -- designed to work on any hardware from a Raspberry Pi to a 256-core server. On production systems, tuning these parameters to match your workload can dramatically improve throughput, reduce latency, and harden security.
This guide covers the most impactful sysctl tunables for FreeBSD 14.x production servers: network stack optimization for high-traffic services, security hardening, ZFS ARC memory management, shared memory for databases, file descriptor limits, and scheduler tuning. Every value listed here has been tested on real FreeBSD production systems.
For broader performance optimization strategies, see our FreeBSD performance tuning guide.
How sysctl Works
The sysctl interface exposes kernel state as a tree of variables. You can read any variable, and write to those marked as writable:
sh# Read a single value sysctl kern.maxfiles # Read an entire subtree sysctl net.inet.tcp # Set a value at runtime (immediate, non-persistent) sysctl net.inet.tcp.sendspace=65536 # List all tunables sysctl -a
Runtime changes with sysctl take effect immediately but are lost on reboot. To make changes persistent, add them to /etc/sysctl.conf:
shvi /etc/sysctl.conf
shellnet.inet.tcp.sendspace=65536
Some parameters are read-only at runtime and can only be set at boot through /boot/loader.conf. These are marked as "loader tunables."
Apply /etc/sysctl.conf without rebooting:
shservice sysctl restart
Network Stack Optimization
These tunables improve network throughput and connection handling for servers running web applications, databases, or any high-connection-count service.
TCP Buffer Sizes
The default TCP send and receive buffers are small. Increase them for higher throughput on modern networks:
sh# /etc/sysctl.conf # Default TCP send/receive buffer sizes (bytes) net.inet.tcp.sendspace=65536 net.inet.tcp.recvspace=65536 # Maximum TCP send/receive buffer sizes (auto-tuning ceiling) net.inet.tcp.sendbuf_max=16777216 net.inet.tcp.recvbuf_max=16777216
FreeBSD auto-tunes buffer sizes per connection, but these set the starting point and ceiling. For 10 Gbps networks, increase sendbuf_max and recvbuf_max to 33554432 (32 MB).
Connection Limits and Backlogs
For servers accepting many simultaneous connections:
sh# Maximum number of pending connections in the listen queue kern.ipc.somaxconn=4096 # Maximum number of half-open (SYN_RCVD) connections net.inet.tcp.syncache.hashsize=1024 net.inet.tcp.syncache.bucketlimit=100 # Increase connection tracking table net.inet.tcp.tcbhashsize=32768
The default somaxconn of 128 causes connection drops under load. Set it to 4096 for busy web servers. NGINX, PostgreSQL, and other services also have their own listen backlog settings that should match.
TCP Performance Features
Enable features that improve throughput and responsiveness:
sh# Enable TCP window scaling (RFC 1323) net.inet.tcp.rfc1323=1 # Enable TCP SACK (selective acknowledgment) net.inet.tcp.sack.enable=1 # Enable TCP Fast Open net.inet.tcp.fastopen.enabled=1 # Reduce TIME_WAIT duration for faster port reuse net.inet.tcp.msl=5000 # Enable port reuse for TIME_WAIT connections net.inet.tcp.maxtcptw=50000 # Nagle algorithm - disable for latency-sensitive services # net.inet.tcp.nodelay=1
TCP Fast Open reduces latency for repeated connections to the same server by allowing data in the SYN packet. Most modern clients support it.
UDP Tuning
For DNS servers, logging pipelines, and game servers:
shnet.inet.udp.maxdgram=65536 net.inet.udp.recvspace=262144
Network Interface Queue Length
Increase the interface send queue for high-throughput interfaces:
sh# Default is 50; increase for 10G+ interfaces net.link.ifqmaxlen=1024
This is a loader tunable. Add to /boot/loader.conf:
shvi /boot/loader.conf
shellnet.link.ifqmaxlen=1024
Security Hardening
These sysctls reduce the attack surface and limit information leakage:
sh# /etc/sysctl.conf # Prevent users from seeing other users' processes security.bsd.see_other_uids=0 security.bsd.see_other_gids=0 # Prevent unprivileged users from reading kernel message buffer security.bsd.unprivileged_read_msgbuf=0 # Disable unprivileged process debugging security.bsd.unprivileged_proc_debug=0 # Randomize PID assignment kern.randompid=1 # Prevent core dumps from setuid programs kern.sugid_coredump=0 # Log all setuid/setgid exec calls security.bsd.see_jail_proc=0 # ICMP settings net.inet.icmp.drop_redirect=1 net.inet.icmp.log_redirect=1 # Ignore ICMP redirects net.inet.ip.redirect=0 net.inet6.ip6.redirect=0 # Do not respond to broadcast pings net.inet.icmp.bmcastecho=0 # Disable IP source routing net.inet.ip.sourceroute=0 net.inet.ip.accept_sourceroute=0 # Black hole packets to closed TCP/UDP ports (stealth mode) net.inet.tcp.blackhole=2 net.inet.udp.blackhole=1
The blackhole settings silently drop packets to closed ports instead of sending RST or ICMP port unreachable. This makes port scanning slower and noisier for attackers.
Stack Guard and ASLR
These are loader tunables in /boot/loader.conf:
sh# /boot/loader.conf # Enable kernel ASLR kern.elf64.aslr.enable=1 kern.elf32.aslr.enable=1 kern.elf64.aslr.pie_enable=1 kern.elf32.aslr.pie_enable=1 # Stack guard pages kern.elf64.aslr.stack=1 kern.elf32.aslr.stack=1
ZFS ARC Tuning
ZFS uses the Adaptive Replacement Cache (ARC) in RAM for caching. By default, ZFS claims up to most of available memory. On servers running memory-hungry applications alongside ZFS, you need to limit the ARC.
These are loader tunables in /boot/loader.conf:
sh# /boot/loader.conf # Maximum ARC size (in bytes). Set to ~50% of RAM for database servers. # Example: 8 GB on a 16 GB server vfs.zfs.arc.max=8589934592 # Minimum ARC size (in bytes). Prevents ARC from shrinking too small. # Example: 1 GB vfs.zfs.arc.min=1073741824
Runtime-adjustable ARC parameters in /etc/sysctl.conf:
sh# Prefer metadata caching over data caching vfs.zfs.arc.meta_limit_percent=75 # Tune ARC eviction behavior vfs.zfs.vdev.cache.size=10485760
Check current ARC usage:
shsysctl vfs.zfs.arc.max sysctl kstat.zfs.misc.arcstats.size sysctl kstat.zfs.misc.arcstats.hits sysctl kstat.zfs.misc.arcstats.misses
Calculate the hit ratio:
shsysctl kstat.zfs.misc.arcstats.hits kstat.zfs.misc.arcstats.misses
A healthy ARC has a hit ratio above 90%. If it is consistently below 80%, the ARC is too small for your workload.
Shared Memory for Databases
PostgreSQL, MySQL, and other databases use System V shared memory. The defaults are too low for production databases.
sh# /etc/sysctl.conf # Maximum shared memory segment size (bytes) # Set to 50-75% of RAM for a dedicated database server kern.ipc.shmmax=4294967296 # Maximum total shared memory (pages, 4096 bytes each) kern.ipc.shmall=1048576 # Maximum shared memory segments per process kern.ipc.shmseg=256 # Semaphore limits for PostgreSQL kern.ipc.semmni=256 kern.ipc.semmns=512 kern.ipc.semmnu=256
For PostgreSQL specifically, also increase:
sh# Maximum message queue bytes kern.ipc.msgmnb=65536 # Maximum message queue messages kern.ipc.msgmni=128
After changing shared memory settings, restart the database:
shservice postgresql restart
File Descriptor Limits
Servers handling many connections (web servers, proxies, database connection pools) need high file descriptor limits:
sh# /etc/sysctl.conf # Maximum open files system-wide kern.maxfiles=200000 # Maximum open files per process kern.maxfilesperproc=100000
Also set per-user limits in /etc/login.conf:
shvi /etc/login.conf
Find the default class and adjust:
shelldefault:\ :openfiles=unlimited:\ :maxproc=unlimited:\ :tc=auth:
Rebuild the login database:
shcap_mkdb /etc/login.conf
Verify the limits:
shsysctl kern.maxfiles ulimit -n
Scheduler and Process Tuning
For server workloads that prioritize throughput over interactive responsiveness:
sh# /etc/sysctl.conf # Increase maximum processes kern.maxproc=10000 # Increase maximum threads per process kern.threads.max_threads_per_proc=4096 # Scheduler timeslice (microseconds). Higher = less context switching kern.sched.slice=12
For interactive workloads (desktops, development machines):
sh# Lower slice for more responsive interactive feel kern.sched.slice=3 kern.sched.interact=30 kern.sched.preempt_thresh=80
Disk I/O Tuning
For servers with heavy disk I/O:
sh# /etc/sysctl.conf # Increase vnode cache kern.maxvnodes=400000 # Async I/O threads vfs.aio.max_aio_per_proc=128 vfs.aio.max_aio_queue_per_proc=128 # Read-ahead tuning (loader tunable) # /boot/loader.conf vfs.read_max=128
For UFS filesystems:
sh# Enable softupdates journaling (already default on modern FreeBSD) # Increase directory name lookup cache vfs.ufs.dirhash_maxmem=67108864
Applying and Verifying Changes
After editing /etc/sysctl.conf:
sh# Apply all settings service sysctl restart # Verify a specific setting sysctl net.inet.tcp.sendspace
After editing /boot/loader.conf, reboot:
shshutdown -r now
To test a change before making it persistent:
shsysctl net.inet.tcp.sendspace=65536
Run your benchmarks, then add to /etc/sysctl.conf if the results are positive.
Monitoring the Effect of Changes
Use built-in tools to verify tuning is working:
sh# Network statistics netstat -s -p tcp | head -30 # Current connection count netstat -an | grep ESTABLISHED | wc -l # File descriptor usage sysctl kern.openfiles # ZFS ARC stats sysctl kstat.zfs.misc.arcstats.hits kstat.zfs.misc.arcstats.misses # Memory usage top -b -n 1 | head -10 # Interrupt rate vmstat 1 5
Complete Production Configuration
Here is a combined /etc/sysctl.conf for a typical FreeBSD web/database server:
sh# Network net.inet.tcp.sendspace=65536 net.inet.tcp.recvspace=65536 net.inet.tcp.sendbuf_max=16777216 net.inet.tcp.recvbuf_max=16777216 kern.ipc.somaxconn=4096 net.inet.tcp.rfc1323=1 net.inet.tcp.sack.enable=1 net.inet.tcp.fastopen.enabled=1 net.inet.tcp.msl=5000 net.inet.tcp.maxtcptw=50000 # Security security.bsd.see_other_uids=0 security.bsd.see_other_gids=0 security.bsd.unprivileged_read_msgbuf=0 security.bsd.unprivileged_proc_debug=0 kern.randompid=1 net.inet.icmp.drop_redirect=1 net.inet.ip.redirect=0 net.inet.tcp.blackhole=2 net.inet.udp.blackhole=1 net.inet.icmp.bmcastecho=0 net.inet.ip.sourceroute=0 net.inet.ip.accept_sourceroute=0 # File descriptors kern.maxfiles=200000 kern.maxfilesperproc=100000 # Shared memory (for PostgreSQL) kern.ipc.shmmax=4294967296 kern.ipc.shmall=1048576 kern.ipc.shmseg=256 kern.ipc.semmni=256 kern.ipc.semmns=512 # Processes kern.maxproc=10000
And the corresponding /boot/loader.conf:
sh# ZFS ARC vfs.zfs.arc.max=8589934592 vfs.zfs.arc.min=1073741824 # Network net.link.ifqmaxlen=1024 # Security (ASLR) kern.elf64.aslr.enable=1 kern.elf64.aslr.pie_enable=1 # Disk I/O vfs.read_max=128
Troubleshooting
System becomes unresponsive after sysctl changes:
Boot into single-user mode and comment out the offending line in /etc/sysctl.conf:
sh# At boot menu, choose single user mode mount -u / vi /etc/sysctl.conf reboot
"sysctl: unknown oid" error:
The parameter may require a kernel module to be loaded first (e.g., ZFS tunables require ZFS), or the parameter name may have changed between FreeBSD versions. Check the FreeBSD manual:
shman tuning man sysctl
Cannot increase kern.maxfiles above a certain value:
Some limits depend on other limits. Ensure kern.maxproc is also high enough, as file descriptor tables are allocated per process.
FAQ
Will these settings survive a FreeBSD upgrade?
Yes. Both /etc/sysctl.conf and /boot/loader.conf are user configuration files that freebsd-update does not overwrite. They persist across upgrades.
Can sysctl changes crash the system?
Setting invalid values for some parameters can cause instability. Always test changes one at a time and verify system stability before making them persistent. The most dangerous tunables are memory-related (ARC, shared memory) where setting values larger than available RAM causes the system to thrash.
Should I tune sysctl values on virtual machines?
Yes, but with caveats. Network buffer sizes still matter. ZFS ARC limits are important because VMs typically have less RAM. Some CPU scheduler tunables have reduced effect because the hypervisor controls actual scheduling.
How do I find all available sysctl variables?
Run sysctl -a for runtime variables. For loader tunables, check sysctl -aT or the FreeBSD Handbook. Many tunables are documented in man tuning(7).
What is the difference between /etc/sysctl.conf and /boot/loader.conf?
/etc/sysctl.conf is processed after the kernel boots. Values can be changed at runtime with the sysctl command. /boot/loader.conf is processed by the bootloader before the kernel starts. These are "loader tunables" that can only be set at boot time -- they configure kernel subsystems that initialize before sysctl.conf is read.
Do I need to tune sysctl for jails?
Jails inherit most sysctl values from the host. Some parameters can be set per-jail using jail.conf settings. Network tunables set on the host apply to jail traffic as well. ZFS ARC is shared between the host and all jails.