FreeBSD.software
Home/Guides/FreeBSD Server Administration: Complete Guide (2026)
guide·2026-04-09·18 min read

FreeBSD Server Administration: Complete Guide (2026)

The complete FreeBSD server administration reference: installation, initial setup, user management, package management, service management, storage with ZFS, logging, monitoring, backup, automation, and maintenance. Everything a sysadmin needs.

FreeBSD powers some of the busiest infrastructure on the internet --- Netflix, WhatsApp, Juniper routers --- because it rewards methodical administration with rock-solid uptime. This guide is the single reference you need to set up, manage, and maintain a FreeBSD server from first boot to production steady-state.

Every command here is FreeBSD-specific. If a path starts with /usr/local/etc/, a tool is sysrc instead of systemctl, or a filesystem is ZFS instead of ext4, that is intentional. This is not a Linux guide with the names swapped.

1. Initial Server Setup

The first 15 minutes after installation determine whether your server is hardened or exposed. Work through this checklist before anything else.

Post-Install Checklist

sh
# Set the hostname sysrc hostname="srv01.example.com" hostname srv01.example.com # Set the timezone tzsetup # Verify the date is correct ntpq -p # If NTP is not yet enabled: sysrc ntpd_enable="YES" sysrc ntpd_sync_on_start="YES" service ntpd start

Update /etc/resolv.conf if DNS was not configured during install:

sh
cat > /etc/resolv.conf << 'EOF' nameserver 1.1.1.1 nameserver 9.9.9.9 EOF

Verify network connectivity and DNS resolution:

sh
ping -c 3 freebsd.org

Console and Locale

Set your locale in /etc/login.conf under the default class, or per-user in ~/.login_conf. For a UTF-8 English environment:

sh
# Add to /etc/login.conf under the default class :charset=UTF-8:\ :lang=en_US.UTF-8:

Then rebuild the database:

sh
cap_mkdb /etc/login.conf

SSH Hardening

Edit /etc/ssh/sshd_config:

sh
# Disable root login and password authentication PermitRootLogin no PasswordAuthentication no ChallengeResponseAuthentication no UseDNS no

Apply and restart:

sh
service sshd restart

Before you lock yourself out: verify your SSH key works in a second terminal session.

Copy your public key to the server if you have not already:

sh
# From your local machine ssh-copy-id user@srv01.example.com

For additional protection, change the default SSH port and enable rate-limiting in your firewall (covered below). Consider also setting MaxAuthTries 3 and LoginGraceTime 30 in sshd_config to limit brute-force attempts.

Firewall Quick Start

FreeBSD ships three firewalls. PF is the most common choice:

sh
# Enable PF sysrc pf_enable="YES" sysrc pflog_enable="YES"

Create /etc/pf.conf with a minimal ruleset:

sh
ext_if = "vtnet0" set block-policy drop set skip on lo0 # Normalize traffic scrub in all # Default deny block all # Allow outbound pass out quick on $ext_if proto { tcp udp icmp } from any to any # Allow SSH pass in on $ext_if proto tcp from any to any port 22 # Allow HTTP/HTTPS pass in on $ext_if proto tcp from any to any port { 80 443 }
sh
service pf start pfctl -f /etc/pf.conf

Install Essential Tools

sh
pkg install -y sudo tmux vim-console

Consider tmux essential for remote administration --- it survives disconnections and lets you split sessions.

2. User and Group Management

FreeBSD provides adduser for interactive creation and pw for scripted administration.

Creating Users

sh
# Interactive (guided prompts) adduser # Scripted: create a deploy user in the wheel group pw useradd deploy -m -G wheel -s /bin/sh -c "Deploy account"

Managing Groups

sh
# Create a group pw groupadd webteam # Add a user to an existing group pw groupmod webteam -m deploy # List groups a user belongs to id deploy # Remove a user from a group pw groupmod webteam -d deploy

Removing Users

sh
# Remove user, home directory, and mail spool pw userdel deploy -r # Lock an account without deleting it (disable login) pw lock deploy # Unlock pw unlock deploy

Granting Privileges with sudo or doas

Install sudo and add the user to the wheel group:

sh
pkg install -y sudo visudo # Uncomment: %wheel ALL=(ALL:ALL) ALL

For a lighter alternative, use doas:

sh
pkg install -y doas echo "permit persist :wheel" > /usr/local/etc/doas.conf

Login Classes

Login classes in /etc/login.conf control resource limits, environment variables, and authentication settings per group of users:

sh
# View the default class cap_mkdb /etc/login.conf pw usermod deploy -L staff

After editing /etc/login.conf, always rebuild the database:

sh
cap_mkdb /etc/login.conf

3. Package Management

FreeBSD's pkg is a binary package manager. It is fast, dependency-aware, and covers the vast majority of use cases. For a deeper comparison with ports, see the pkg vs. ports guide.

Core Commands

sh
# Install a package pkg install nginx # Remove a package and its orphaned dependencies pkg delete -Ry nginx # Upgrade all packages pkg upgrade # Search for packages pkg search redis # Show package info pkg info redis # Audit installed packages for vulnerabilities pkg audit -F

Repository Configuration

Custom repositories go in /usr/local/etc/pkg/repos/. To use the latest quarterly branch:

sh
mkdir -p /usr/local/etc/pkg/repos cat > /usr/local/etc/pkg/repos/FreeBSD.conf << 'EOF' FreeBSD: { url: "pkg+http://pkg.FreeBSD.org/${ABI}/quarterly", mirror_type: "srv", enabled: yes } EOF pkg update -f

Locking Packages

Prevent critical packages from being upgraded accidentally:

sh
pkg lock nginx pkg lock -l # list locked packages pkg unlock nginx # remove the lock

4. Ports Collection

Ports let you compile software from source with custom options --- useful when you need a non-default build flag or a package not available as a binary. See the full ports guide for deeper coverage.

When to Use Ports

Use ports when you need:

  • A compile-time option not enabled in the binary package (e.g., nginx with a specific module).
  • A newer version than what quarterly provides.
  • Custom optimization flags.

For everything else, use pkg.

Getting the Ports Tree

sh
# Using git (recommended on FreeBSD 14+) pkg install -y git-lite git clone --depth 1 https://git.freebsd.org/ports.git /usr/ports # Update later cd /usr/ports && git pull

Building a Port

sh
cd /usr/ports/www/nginx make config # choose compile options interactively make install clean

To set options non-interactively:

sh
cd /usr/ports/www/nginx make -DBATCH install clean

Mixing pkg and Ports

Avoid installing the same software from both pkg and ports --- the package database can get confused. If you need one port compiled from source, consider using poudriere to build your own package repository. That way, all machines get consistent binary packages, including your custom builds.

Cleaning Up

sh
# Remove work directories from all ports cd /usr/ports && make clean NOCLEANDEPENDS=yes # Remove downloaded distfiles older than 30 days pkg clean -y

5. Service Management

FreeBSD uses the rc system. There is no systemd. Services are controlled by /etc/rc.conf, the sysrc utility, and the service command.

Enabling and Starting Services

sh
# Enable a service at boot sysrc nginx_enable="YES" # Start immediately service nginx start # Check status service nginx status # Restart service nginx restart # One-shot start without enabling at boot service nginx onestart

Listing Services

sh
# All enabled services service -e # All available services service -l # Check what rc.conf currently sets sysrc -a

Custom rc.d Scripts

If you write your own daemon, place its rc script in /usr/local/etc/rc.d/ and follow the template:

sh
#!/bin/sh # PROVIDE: myapp # REQUIRE: LOGIN DAEMON NETWORKING # KEYWORD: shutdown . /etc/rc.subr name="myapp" rcvar="myapp_enable" command="/usr/local/bin/myapp" pidfile="/var/run/${name}.pid" load_rc_config $name run_rc_command "$1"
sh
chmod +x /usr/local/etc/rc.d/myapp sysrc myapp_enable="YES" service myapp start

Debugging Service Issues

When a service fails to start, check these in order:

sh
# Check the rc script for syntax errors sh -n /usr/local/etc/rc.d/myapp # Start with verbose output service myapp start && echo "OK" || echo "FAILED" # Check logs tail -50 /var/log/messages | grep myapp # Verify the binary exists and is executable ls -la /usr/local/bin/myapp # Check if the port it needs is already in use sockstat -l | grep 8080

rc.conf Best Practices

Keep /etc/rc.conf clean. Use sysrc instead of hand-editing --- it handles quoting and prevents duplicate entries. For complex setups, split configuration into /etc/rc.conf.d/ files:

sh
# Instead of cramming everything into rc.conf: mkdir -p /etc/rc.conf.d echo 'nginx_enable="YES"' > /etc/rc.conf.d/nginx echo 'nginx_flags="-g daemon off;"' >> /etc/rc.conf.d/nginx

6. Storage with ZFS

ZFS is the default and recommended filesystem on FreeBSD. It combines volume management and filesystem features into a single, coherent tool. For the full deep-dive, see the ZFS on FreeBSD guide.

Pool Creation

sh
# Single disk (not recommended for production) zpool create tank /dev/da0 # Mirror (2 disks) zpool create tank mirror /dev/da0 /dev/da1 # RAIDZ1 (3+ disks, single parity) zpool create tank raidz /dev/da0 /dev/da1 /dev/da2 # Check pool status zpool status

Datasets

Datasets replace traditional partitions. Each can have its own mountpoint, compression, quota, and reservation:

sh
zfs create tank/data zfs create tank/data/www zfs set compression=lz4 tank/data zfs set quota=50G tank/data/www zfs set mountpoint=/var/www tank/data/www

Snapshots

Snapshots are instant, nearly free, and the foundation of every backup strategy on ZFS:

sh
# Create a snapshot zfs snapshot tank/data/www@2026-04-09 # List snapshots zfs list -t snapshot # Rollback to a snapshot (destroys changes since) zfs rollback tank/data/www@2026-04-09 # Destroy a snapshot zfs destroy tank/data/www@2026-04-09

Scrubs

Scrubs verify data integrity by reading every block and comparing it against its checksum. Schedule them regularly:

sh
# Run a scrub now zpool scrub tank # Check scrub status zpool status tank

Add a monthly scrub to cron or periodic (covered in Section 10).

Useful ZFS Properties

sh
# Check space usage per dataset zfs list -o name,used,avail,refer,mountpoint # See compression ratios zfs get compressratio tank/data # Enable access time tracking (off by default for performance) zfs set atime=off tank/data # Set record size for database workloads (PostgreSQL, MySQL) zfs create -o recordsize=16k -o primarycache=metadata tank/data/pgdata

Boot Environments

Boot environments let you snapshot the entire OS before an upgrade, then revert instantly if something goes wrong. This is one of FreeBSD's most powerful features. See the boot environments guide.

sh
pkg install -y bectl bectl create pre-upgrade bectl list # If the upgrade fails: bectl activate pre-upgrade reboot

7. System Logging

syslogd

FreeBSD uses syslogd by default. Configuration lives in /etc/syslog.conf:

sh
# Log all auth messages to a separate file auth.* /var/log/auth.log authpriv.* /var/log/auth.log # Log mail mail.* /var/log/maillog

Restart after changes:

sh
service syslogd restart

newsyslog: Log Rotation

FreeBSD rotates logs with newsyslog, configured in /etc/newsyslog.conf:

sh
# Format: logfile [owner:group] mode count size(KB) when flags [/pid_file] [sig_num] /var/log/auth.log 600 7 1000 * JC /var/log/nginx-access.log www:www 644 52 * @T00 JC /var/run/nginx.pid 30

Flags: J = bzip2, C = create if missing, Z = gzip. The @T00 rotates at midnight.

Test rotation without waiting:

sh
newsyslog -vn # dry run newsyslog -v # run now

Remote Logging

For multi-server environments, forward logs to a central syslog server. On the client, add to /etc/syslog.conf:

sh
*.* @loghost.example.com

On the log server, enable remote reception in /etc/rc.conf:

sh
sysrc syslogd_flags="-a logclient.example.com" service syslogd restart

Quick Log Analysis

sh
# Failed SSH logins grep "Failed password" /var/log/auth.log | tail -20 # Recent service restarts grep "restart" /var/log/messages | tail -20 # Kernel messages (hardware errors, driver issues) dmesg | tail -40 # Show all logins last -10 # Show failed logins lastlogin

8. System Monitoring

A quick reference for built-in monitoring tools. For a comprehensive setup including Prometheus, Grafana, and alerting, see the server monitoring guide.

top --- Process Activity

sh
top -SHP # show system processes, per-CPU stats, thread view top -m io # I/O mode (shows disk read/write per process)

vmstat --- Virtual Memory

sh
vmstat 1 5 # 5 samples at 1-second intervals

Key columns: r (runnable processes), b (blocked), si/so (swap in/out --- should be zero), us/sy (user/system CPU%).

iostat --- Disk I/O

sh
iostat -x -w 1 # extended stats, 1-second interval

Watch %busy and ms/t (milliseconds per transaction). If a disk is consistently above 80% busy, you have a bottleneck.

gstat --- GEOM Statistics

sh
gstat # real-time disk I/O in a curses interface gstat -d # show only disks with activity

netstat --- Network

sh
netstat -an # all connections, numeric netstat -m # mbuf usage (memory buffers for networking) systat -ifstat 1 # real-time interface throughput

swapinfo --- Swap Usage

sh
swapinfo -h # human-readable swap usage

If swap is consistently in use, you need more RAM or have a memory leak. On ZFS systems, swap usage is especially problematic because ZFS relies heavily on ARC (Adaptive Replacement Cache).

Quick Health Check Script

A one-liner you can run any time to get a snapshot of system health:

sh
echo "=== Uptime ===" && uptime && \ echo "=== Disk ===" && zpool status -x && \ echo "=== Memory ===" && top -b -d 1 | head -5 && \ echo "=== Swap ===" && swapinfo -h && \ echo "=== Services ===" && service -e | wc -l && \ echo "=== Pkg Audit ===" && pkg audit -q | wc -l

9. Backup Strategy

No server is production-ready without backups. The right strategy depends on your storage and recovery requirements. For full coverage, see the backup guide.

ZFS Send/Receive

The fastest and most space-efficient method for ZFS systems:

sh
# Full send to a file zfs snapshot tank/data@backup-20260409 zfs send tank/data@backup-20260409 > /backup/tank-data-full.zfs # Incremental send (requires a prior snapshot) zfs send -i tank/data@backup-20260408 tank/data@backup-20260409 > /backup/tank-data-incr.zfs # Send to a remote host over SSH zfs send tank/data@backup-20260409 | ssh backup-host zfs receive backup/data # Receive zfs receive tank/restored < /backup/tank-data-full.zfs

tarsnap

Tarsnap provides encrypted, deduplicated offsite backups:

sh
pkg install -y tarsnap tarsnap-keygen --keyfile /root/tarsnap.key --user you@example.com --machine srv01 # Create a backup tarsnap -c -f srv01-etc-20260409 /etc /usr/local/etc # List archives tarsnap --list-archives | sort # Restore tarsnap -x -f srv01-etc-20260409 -C /tmp/restore

rsync

rsync works well for file-level replication to a remote host:

sh
pkg install -y rsync rsync -avz --delete /var/www/ backup-host:/backup/www/

The 3-2-1 Rule

Every backup strategy should follow the 3-2-1 principle: three copies of your data, on two different media types, with one offsite. On FreeBSD, a practical implementation looks like this:

  1. Live data on ZFS with regular snapshots (copy 1, local).
  2. ZFS send/receive to a second server or NAS on a different disk (copy 2, local but different media).
  3. Tarsnap to encrypted cloud storage (copy 3, offsite).

Boot Environments as Safety Nets

Before any upgrade or risky change:

sh
bectl create pre-change # ... do the work ... # If it breaks: bectl activate pre-change reboot

Testing Your Backups

A backup you have never restored is not a backup. Schedule quarterly restore tests:

sh
# Create a temporary dataset for testing zfs create tank/restore-test # Restore a snapshot zfs receive tank/restore-test < /backup/tank-data-full.zfs # Verify the data ls /tank/restore-test/ diff -r /var/www/ /tank/restore-test/www/ # Clean up zfs destroy -r tank/restore-test

10. Automation

cron

User crontabs are edited with crontab -e. System-wide cron jobs go in /etc/crontab or as files in /var/cron/tabs/. For a deeper look at scheduling, see the cron and periodic guide.

sh
# Edit root's crontab crontab -e # Example: ZFS snapshot every 6 hours 0 */6 * * * /sbin/zfs snapshot tank/data@auto-$(date +\%Y\%m\%d-\%H\%M) # Example: Clean old snapshots weekly 0 3 * * 0 /sbin/zfs list -t snapshot -o name -H | grep "auto-" | head -n -28 | xargs -n1 zfs destroy

periodic

FreeBSD's periodic system runs daily, weekly, and monthly maintenance scripts. Configuration lives in /etc/periodic.conf:

sh
# /etc/periodic.conf daily_clean_tmps_enable="YES" daily_status_security_enable="YES" daily_status_pkg_changes_enable="YES" weekly_locate_enable="YES" monthly_accounting_enable="YES"

Add custom scripts to /usr/local/etc/periodic/daily/, /weekly/, or /monthly/.

Example custom periodic script (/usr/local/etc/periodic/daily/450.zfs-snapshot):

sh
#!/bin/sh # Daily ZFS snapshot rotation echo "" echo "Daily ZFS snapshot:" DATASET="tank/data" SNAP="${DATASET}@daily-$(date +%Y%m%d)" KEEP=7 /sbin/zfs snapshot "${SNAP}" && echo " Created ${SNAP}" # Remove snapshots older than $KEEP days /sbin/zfs list -t snapshot -o name -H | grep "${DATASET}@daily-" | \ sort | head -n -${KEEP} | while read snap; do /sbin/zfs destroy "${snap}" && echo " Destroyed ${snap}" done
sh
chmod +x /usr/local/etc/periodic/daily/450.zfs-snapshot

Ansible Basics

For managing multiple FreeBSD servers, Ansible works well. Install it on your control node (not the target):

sh
# On the control node pip install ansible # Inventory file cat > inventory.ini << 'EOF' [freebsd] srv01 ansible_host=10.0.0.1 ansible_python_interpreter=/usr/local/bin/python3 srv02 ansible_host=10.0.0.2 ansible_python_interpreter=/usr/local/bin/python3 EOF # Ensure Python is on the targets ansible freebsd -i inventory.ini -m raw -a "pkg install -y python3"

A simple playbook to harden SSH and enable PF:

sh
# harden.yml --- - hosts: freebsd become: yes tasks: - name: Disable root SSH login lineinfile: path: /etc/ssh/sshd_config regexp: '^PermitRootLogin' line: 'PermitRootLogin no' notify: restart sshd - name: Enable PF firewall community.general.sysrc: name: pf_enable value: "YES" handlers: - name: restart sshd service: name: sshd state: restarted

For a detailed Ansible-on-FreeBSD walkthrough, see the Ansible setup guide.

11. System Updates

Keeping the base system and packages current is non-negotiable. For a full walkthrough including major version upgrades, see the FreeBSD update guide.

Base System: freebsd-update

sh
# Fetch and install security patches freebsd-update fetch install # Upgrade to a new release (e.g., 14.1 to 14.2) freebsd-update -r 14.2-RELEASE upgrade freebsd-update install reboot freebsd-update install # run again after reboot for userland

Packages: pkg upgrade

sh
# Always create a boot environment first bectl create pre-pkg-upgrade # Upgrade all packages pkg upgrade # If something breaks bectl activate pre-pkg-upgrade reboot

Major Version Upgrades

Major version jumps (e.g., 13.x to 14.x) require care:

  1. Read the release notes and UPDATING file.
  2. Create a boot environment.
  3. Run freebsd-update -r 14.0-RELEASE upgrade.
  4. Rebuild all ports or pkg upgrade -f after the base upgrade.
  5. Test thoroughly before destroying the old boot environment.

12. Maintenance Calendar

A disciplined schedule prevents drift and downtime.

Daily

  • Review /var/log/messages and /var/log/auth.log for anomalies.
  • Check periodic daily output in /var/mail/root or your configured destination.
  • Verify services are running: service -e and spot-check with service status.

Weekly

  • Run pkg audit -F to check for known vulnerabilities.
  • Run pkg upgrade on staging, then production if clean.
  • Review firewall logs: tcpdump -n -e -ttt -r /var/log/pflog | tail -50.
  • Verify backup integrity: restore a snapshot to a test location.

Monthly

  • Run zpool scrub tank on every pool.
  • Review disk health: smartctl -a /dev/da0 (requires smartmontools).
  • Rotate tarsnap archives: delete backups older than your retention policy.
  • Review and prune user accounts.
  • Apply freebsd-update fetch install for any base system patches.

Quarterly

  • Review /etc/periodic.conf and /etc/syslog.conf for accuracy.
  • Test full disaster recovery: restore from backup to bare metal or a VM.
  • Audit installed packages: pkg prime-list shows manually installed packages.
  • Review and update firewall rules.

13. FAQ

How do I find which package provides a specific file?

sh
pkg which /usr/local/bin/tmux # Output: /usr/local/bin/tmux was installed by package tmux-3.5a

If the file is not installed yet:

sh
pkg provides bin/tmux # requires pkg-provides plugin

How do I recover from a failed package upgrade?

If you created a boot environment before upgrading (and you should always do this):

sh
bectl activate pre-pkg-upgrade reboot

If you did not, try rolling back with pkg:

sh
pkg lock -a # lock everything pkg unlock <broken-package> # unlock just the broken one pkg install <broken-package> # reinstall the specific version

How do I check which services are listening on which ports?

sh
sockstat -l4 # listening IPv4 sockets sockstat -l6 # listening IPv6 sockets sockstat -l # both

What is the difference between /etc/ and /usr/local/etc/?

/etc/ contains configuration for the base system --- the OS itself (SSH, syslog, rc.conf, login.conf). /usr/local/etc/ contains configuration for third-party software installed via pkg or ports (nginx, PostgreSQL, Redis). This separation is deliberate: you can wipe and reinstall all packages without touching base system configuration, and vice versa.

How do I tell if my server needs more RAM?

Check ARC (ZFS cache) and swap usage:

sh
# ARC statistics sysctl kstat.zfs.misc.arcstats.size | awk '{print $2/1024/1024/1024 " GB"}' sysctl vfs.zfs.arc_min vfs.zfs.arc_max # Swap usage (should be zero in normal operation) swapinfo -h # Page faults and memory pressure vmstat 1 5

If swapinfo shows consistent usage, or vmstat shows high pi/po (page in/out) values, you need more RAM. On ZFS systems, aim for at least 1 GB of RAM per TB of storage, plus whatever your applications need.


Where to Go Next

This guide is a living reference. Bookmark it, revisit the maintenance calendar monthly, and treat every section as a checklist --- not a suggestion.

Get more FreeBSD guides

Weekly tutorials, security advisories, and package updates. No spam.