🗂️ The FreeBSD Corner Cheat Sheet: Every Command in One Place
A reading order, a "what do I want to do?" finder, a Linux-to-FreeBSD map, the file locations, the golden rules, and the cheat sheet of every page in one place
How to use this page
The FreeBSD Corner has nineteen topic pages. Each one ends with a cheat sheet, and each command in them was run for real on the page it belongs to, on FreeBSD 15.0-RELEASE. This page collects them all, plus four things that no single page has: a reading order, a finder that starts from what you want to do, a map from Linux commands, and the golden rules that kept coming back. Every item links to the page with the explanation, the real output and the traps.
If something here is marked "from the documentation", it means the page that owns it says so: we did not run it. Trust the page, not this summary, for details.
A reading order
Pick the path that matches your job. Each is a list of pages in a sensible order.
If you know Linux and are new to FreeBSD:
- FreeBSD vs Linux: what is different, and the map of where things are.
- Upgrading: patch it, undo it, and learn boot environments.
- ZFS in practice: pools, datasets, snapshots, and repair.
- Jails: your first isolated service.
- Hardening: what to switch off and lock down.
If you are building a server that hosts services:
- Jails and Containers: fence the services in.
- pf or ipfw: choose a firewall and learn its order of rules.
- Ports and poudriere: your own software, built the clean way.
- Hardening, Boot and tuning and Monitoring: lock it down, tune it, and watch it.
- ZFS backups: copy the data to another machine, and prove you can restore it.
- WireGuard: connect the machines with an encrypted tunnel.
- NFS: share a dataset with other machines.
If you want virtual machines and containers:
- bhyve and vm-bhyve: full virtual machines.
- Containers: containers from standard images.
- Linux compatibility: running Linux programs and images.
- Cloud: machines that configure themselves at first boot.
If you build software from source:
- Ports: the ports tree and its options.
- poudriere: a clean build farm with your own package repository.
- Containers: ship what you built as an image.
What do I want to do?
Start from the job, and go to the page:
- Know what version I am running: uname -a, freebsd-version -kru. (see FreeBSD vs Linux)
- Make a restore point before changing anything: bectl create NAME. (see Upgrading FreeBSD Safely)
- Patch the base system: freebsd-update fetch, freebsd-update install, reboot, and install again if asked. (see Upgrading FreeBSD Safely)
- Upgrade to the next release: freebsd-update upgrade -r 15.1-RELEASE, then the install passes. (see Upgrading FreeBSD Safely)
- Undo an upgrade: bectl activate NAME, then reboot. (see Upgrading FreeBSD Safely)
- See how busy the machine is right now: uptime, top -b, vmstat 1 3, iostat -x -c 2 -w 1 DISK. (see Monitoring FreeBSD)
- Check storage health at a glance: zpool status -x (it says all pools are healthy). (see Monitoring FreeBSD)
- Copy my data to another machine: zfs snapshot, then zfs send -i @OLD DATA@NEW | ssh USER@HOST zfs receive -u COPY. (see ZFS Backups to Another Machine)
- Get my data back after a disaster: ssh USER@HOST zfs send COPY@NAME | zfs receive DATA, then compare checksums. (see ZFS Backups to Another Machine)
- Connect two machines with an encrypted tunnel: wg genkey, a wg0.conf, wg setconf wg0 FILE, ifconfig wg0 inet ADDRESS/24 up, check with wg show. (see WireGuard on FreeBSD)
- Make a tunnel come back after a reboot: sysrc cloned_interfaces+=wg0, ifconfig_wg0, and /etc/start_if.wg0 with the wg setconf line. (see WireGuard on FreeBSD)
- Share a directory with other machines: zfs set sharenfs="-network NET/24" DATASET, the V4: line in /etc/exports, then mount -t nfs -o nfsv4 SERVER:/path /mnt on the client. (see File Sharing with NFS)
- Mount a share at boot: a line SERVER:/path /mnt nfs rw,nfsv4,late 0 0 in /etc/fstab, and sysrc nfs_client_enable=YES. (see File Sharing with NFS)
- Get a nightly health report: periodic daily, with daily_output and daily_status_zfs_enable set in /etc/periodic.conf. (see Monitoring FreeBSD)
- Run my own check every few minutes and log problems: a small script with logger -p daemon.warning, run from crontab. (see Monitoring FreeBSD)
- Collect metrics and alerts: node_exporter and prometheus, bound to 127.0.0.1, checked with promtool check config. (see Monitoring FreeBSD)
- Check for vulnerable packages: pkg audit -F (and pkg -j NAME audit -F for each jail). (see Hardening FreeBSD)
- See what is listening on the network: sockstat -4l -6l. (see Hardening FreeBSD)
- Start a service now, and at boot: service NAME start, sysrc NAME_enable=YES. (see FreeBSD vs Linux)
- Install software the ready-made way: pkg install NAME. (see FreeBSD vs Linux)
- Install software built to my options: make config, make install clean BATCH=yes in the port, or build it once with poudriere bulk. (see The Ports Tree)
- Block or allow network traffic: pf: check with pfctl -nf /etc/pf.conf, load with pfctl -f /etc/pf.conf. ipfw: ipfw add NUMBER allow|deny .... (see The pf Firewall)
- Ban one address without reloading the rules: pf: pfctl -t NAME -T add ADDRESS. ipfw: ipfw table NAME add ADDRESS. (see ipfw)
- Slow a connection down to test an application: ipfw pipe 1 config bw 400Kbit/s, and a pipe rule numbered early. (see ipfw)
- Fence a service into its own environment: zfs create zroot/jails/NAME, a block in /etc/jail.conf, service jail start NAME. (see Jails)
- Run a standard container image: podman run --rm --net host --os freebsd IMAGE COMMAND. (see Containers on FreeBSD)
- Copy a dataset or keep a point in time: zfs snapshot DATASET@NAME, zfs clone DATASET@NAME NEW, zfs send DATASET@NAME | zfs receive NEW. (see ZFS in Practice)
- Check storage health, and repair: zpool status, zpool scrub POOL. (see ZFS in Practice)
- Run a full virtual machine: vm create, vm install, vm start (or the plain bhyve command line). (see vm-bhyve)
- Run a Linux program: sysrc linux_enable=YES, service linux start, pkg install linux_base-rl9. (see Linux Binary Compatibility)
- Run a Linux container image: podman pull --os linux --arch amd64 IMAGE, podman run --os linux .... (see Containers on FreeBSD)
- Change a kernel setting now, for good, or only at boot: sysctl NAME=VALUE; add it to /etc/sysctl.conf; or put NAME="VALUE" in /boot/loader.conf.d/. (see The Boot Loader and Kernel Tuning)
- Rescue a machine that will not finish booting: boot menu 2 (single-user) or 3 (loader prompt), 8 (boot environments). (see The Boot Loader and Kernel Tuning)
- Lock SSH down: sshd -T, edit, sshd -t, service sshd reload, test from a second connection. (see Hardening FreeBSD)
- Hide processes and logs from other users: security.bsd.see_other_uids=0 and friends. (see Hardening FreeBSD)
- Protect a file even from root: chflags schg FILE, and kern.securelevel to make it stick. (see Hardening FreeBSD)
- Set up a cloud instance without a keyboard: #cloud-config user data on a cidata disk, plus /firstboot. (see FreeBSD in the Cloud)
- Find where a setting comes from: /etc/rc.conf, then /etc/rc.conf.d/, then loader files in /boot/loader.conf.d/. (see The Boot Loader and Kernel Tuning)
From Linux to FreeBSD
The same job, the other tool. (The overview page has the long version with the reasons and the traps.)
- Services: systemctl status NAME is service NAME status. systemctl enable NAME is sysrc NAME_enable=YES.
- Logs: journalctl is tail /var/log/messages (and /var/log/security, /var/log/auth.log). Log rotation: logrotate is newsyslog.
- Packages: apt install is pkg install. apt update && apt upgrade is pkg update && pkg upgrade. apt has no twin for pkg audit.
- Network: ip addr is ifconfig. ip route is netstat -rn. ss -tln is sockstat -4l.
- Memory and modules: free -h is sysctl hw.physmem and top. lsmod is kldstat.
- Root: sudo is su - (or install sudo or doas).
- Firewall: ufw and firewalld are pf (or ipfw). iptables -L -v is pfctl -sr -v. conntrack -L is pfctl -ss. ipset is a table.
- Containers: docker run is podman run (or a jail). Linux programs run through the Linux compatibility layer.
- Virtual machines: qemu and libvirt are bhyve and vm-bhyve.
- Cloud first boot: cloud-init is nuageinit.
- Boot-time kernel settings: what Linux puts on the kernel command line goes in /boot/loader.conf.d/ (or is typed at the loader prompt).
- Scheduled health reports: the nightly job is periodic daily (cron starts it), and cron itself works as you expect (crontab -e, crontab -l).
- Gotchas in the tools: sed -i '' needs an empty backup name, date -d is date -v, there is no bash by default, and /proc is empty unless you mount it.
The cheat sheet of every page
The cheat sheet from the end of each page, in order. Click the title for the page itself.
1. FreeBSD vs Linux: What Is Actually Different
The page: open it.
- Versions: uname -a, freebsd-version -kru.
- Services: service NAME status|start|stop|restart, service -e, sysrc NAME_enable=YES, file /etc/rc.conf.
- Packages: pkg install NAME, pkg search NAME, pkg upgrade. Your software lives in /usr/local.
- Users: group wheel, su -, pw useradd and pw groupshow.
- ZFS: zpool list, zfs list, bectl list, bectl create NAME, bectl destroy NAME.
- Network: ifconfig, netstat -rn, sockstat -4l, ping.
- System: sysctl, kldstat, top, tail /var/log/messages.
- Gotchas: sed -i '' (empty backup name), date -v-1d, no bash, no sudo by default.
2. Upgrading FreeBSD Safely: freebsd-update, pkg and Boot Environments
The page: open it.
- Where am I: freebsd-version -kru (kernel installed, kernel running, userland).
- Safety net: bectl create NAME, bectl list, bectl activate NAME (then reboot), bectl destroy -o NAME.
- Patch update: freebsd-update fetch, freebsd-update install, reboot, freebsd-update install again if asked. If fetch seems stuck at the end, press q.
- Release upgrade: freebsd-update upgrade -r 15.1-RELEASE, freebsd-update install, reboot, freebsd-update install, pkg upgrade, freebsd-update install.
- Packages: pkg update, pkg upgrade, pkg audit -F, pkg autoremove, pkg clean.
- Undo: boot environments hold the OS only. Your data is not rolled back.
3. ZFS in Practice: Pools, Datasets, Snapshots and Self-Healing
The page: open it.
- Pools: zpool create NAME mirror DISK1 DISK2, zpool list, zpool status, zpool history NAME, zpool destroy NAME (careful).
- Datasets: zfs create POOL/NAME, zfs list -r POOL, zfs destroy POOL/NAME.
- Properties: zfs get PROP DATASET, zfs set quota=20m DATASET, zfs set compression=lz4 DATASET.
- Snapshots: zfs snapshot DATASET@NAME, zfs list -t snapshot, zfs diff DATASET@NAME, cp from /MOUNT/.zfs/snapshot/NAME/, zfs rollback DATASET@NAME, zfs destroy DATASET@NAME.
- Clones and copies: zfs clone DATASET@NAME NEW, zfs send DATASET@NAME | zfs receive NEW.
- Health: zpool scrub POOL, zpool status, zpool offline|online POOL DISK, zpool clear POOL, zpool replace POOL OLD NEW.
4. Jails: Lightweight Containers, the BSD Way
The page: open it.
- Create: zfs create zroot/jails/NAME, unpack base.txz into it, copy /etc/resolv.conf.
- Configure: a block in /etc/jail.conf with ip4.addr, path, exec.start, exec.stop.
- Boot at start: sysrc jail_enable=YES and sysrc jail_list="NAME ...".
- Run it: service jail start NAME, service jail stop NAME, jls.
- Inside: jexec NAME COMMAND, ps -J NAME, sockstat -4l on the host.
- Software: pkg -j NAME install PKG, sysrc -j NAME VAR=VALUE, service -j NAME SERVICE start.
- Clone: zfs snapshot zroot/jails/NAME@x, zfs clone zroot/jails/NAME@x zroot/jails/NEW.
- A jailed root cannot: load kernel modules, change the network or clock, or see the host.
5. The pf Firewall: Rules, Tables and Redirects
The page: open it.
- Files and services: /etc/pf.conf, sysrc pf_enable=YES, sysrc pflog_enable=YES, service pf start|stop|status|reload.
- Check and load: pfctl -nf /etc/pf.conf (check only), pfctl -f /etc/pf.conf (load), pfctl -d (disable), pfctl -e (enable).
- Look: pfctl -sr (filter rules), pfctl -sr -v (with counters), pfctl -sn (translation rules), pfctl -ss (states), pfctl -si (statistics).
- Rules: block drop|return in all, pass in on $ext_if proto tcp to port { 80, 443 }, pass out all keep state, quick, log.
- Tables: table <name> persist, pfctl -t NAME -T add|delete|show ADDRESS.
- Redirect: rdr pass on $ext_if proto tcp to port 8080 -> 10.0.2.50 port 80.
- Log: service pflog start, tcpdump -n -e -i pflog0.
- Order: last match wins, except quick. Macros, tables, translation, then filter rules.
6. The Ports Tree: Building FreeBSD Software from Source
The page: open it.
- Tree: /usr/ports/CATEGORY/PORT. Get it with git clone (or from the install disc). whereis NAME finds a program's port.
- Look: make -V VARIABLE, make showconfig, make build-depends-list, make run-depends-list.
- Options: make config (dialog), make config-recursive (with dependencies), saved under /var/db/ports. In /etc/make.conf: category_port_SET=OPTION, category_port_UNSET=OPTION, CPUTYPE?=native.
- Build: make install-missing-packages (dependencies as packages), make install clean BATCH=yes.
- Tidy: pkg autoremove -n, make clean, make distclean.
- After: pkg info NAME (shows Origin and Options), pkg lock NAME, pkg which FILE.
- At scale: poudriere builds your own package repository (see the poudriere page).
7. bhyve: Virtual Machines Built into FreeBSD
The page: open it.
- Ready? grep 'AMD Features2' /var/run/dmesg.boot (look for SVM), kldload vmm, sysrc kld_list+=vmm.
- Disk: zfs create -V 4G -o volmode=dev zroot/vm/NAME gives /dev/zvol/zroot/vm/NAME.
- Network: ifconfig tap0 create, ifconfig bridge0 create, ifconfig bridge0 addm tap0, sysctl net.link.tap.up_on_open=1.
- Boot: bhyveload -c stdio -m 768M -d FILE NAME, then bhyve -c 1 -m 768M -H -A -P -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s 3:0,virtio-blk,DISK -s 4:0,ahci-cd,ISO -l com1,stdio NAME.
- Look: ls /dev/vmm, bhyvectl --vm=NAME --get-stats.
- Stop: halt -p in the guest (bhyve exits with status 1). bhyvectl --vm=NAME --destroy for a stuck guest.
- Firewall: set skip on { lo0 bridge0 } in /etc/pf.conf.
8. poudriere: Build Your Own Packages, Properly
The page: open it.
- Config: /usr/local/etc/poudriere.conf (ZPOOL, ZROOTFS, BASEFS, USE_TMPFS, DISTFILES_CACHE). Options in /usr/local/etc/poudriere.d/make.conf. List ports in a file, one per line (category/port).
- Jail: poudriere jail -c -j NAME -v 15.0-RELEASE [-m tar=base.txz], poudriere jail -l.
- Ports tree: poudriere ports -c -p default -m git (or -m null -M /usr/ports), poudriere ports -u, poudriere ports -l.
- Build: poudriere bulk -n -j NAME -p default -f LIST (dry run), then without -n. poudriere status, poudriere pkgclean -n.
- Repository: /usr/local/poudriere/data/packages/NAME-default/, config in /usr/local/etc/pkg/repos/NAME.conf (make the folder first), pkg update -f, pkg rquery '%n-%v' PKG.
- Install into a jail's folder with the host's config: pkg -r /jails/NAME install PKG (pkg -j NAME uses the jail's own config).
- Safety: sign the repository, serve it over HTTPS, and match the jail's release to your machines.
9. vm-bhyve: Virtual Machines with Short Commands
The page: open it.
- Setup: pkg install vm-bhyve, sysrc vm_enable=YES, sysrc vm_dir="zfs:zroot/vm", vm init, templates from /usr/local/share/examples/vm-bhyve.
- Network: vm switch create -a 10.88.0.1/24 NAME, vm switch list. Let pf skip the bridge vm-NAME.
- Media: vm iso FILE-or-URL, vm iso.
- Guests: vm create -t TEMPLATE -s 4G -c 1 -m 768M NAME, vm list, vm info NAME, vm configure NAME.
- Install: vm install NAME ISO, vm console NAME (leave with ~ then Ctrl-D).
- Run: vm start NAME, vm stop NAME (graceful), vm poweroff NAME (hard), vm destroy NAME.
- ZFS tricks: vm snapshot NAME@SNAP (power off first), vm clone NAME@SNAP NEW.
- Autostart: sysrc vm_list="NAME ...", sysrc vm_delay=5.
- If it will not start: tail /zroot/vm/NAME/vm-bhyve.log, and check the disk's device file in /dev/zvol.
10. ipfw: The Other FreeBSD Firewall
The page: open it.
- Load safely: kenv net.inet.ip.fw.default_to_accept=1, then kldload ipfw.
- Rules: ipfw add NUMBER allow|deny|reset|count|pipe ... from SRC to DST [in|out] [via IF] [setup|established] [keep-state] [log], ipfw list, ipfw -a list (counters), ipfw delete NUMBER, ipfw -f flush.
- Order: lowest number first, first match wins. check-state early.
- Tables: ipfw table NAME create, ipfw table NAME add|delete ADDRESS, ipfw table NAME list, rule from "table(NAME)".
- Log: log in the rule, sysctl net.inet.ip.fw.verbose=1, lines in /var/log/security.
- Shaping: kldload dummynet, ipfw pipe 1 config bw 400Kbit/s, ipfw add 120 pipe 1 ip from ... out. Put it early.
- Permanent: script + sysrc firewall_enable=YES firewall_script=/etc/ipfw.rules firewall_logging=YES, sh -n, service ipfw restart.
- Escape hatches: ipfw -f flush (back to the default rule), kldunload ipfw (removes the firewall), and the console.
11. Linux Binary Compatibility: Running Linux Programs on FreeBSD
The page: open it.
- Switch on: sysrc linux_enable=YES, service linux start (modules linux, linux64; mounts under /compat/linux).
- Userland: pkg install linux_base-rl9 (Rocky Linux 9). More: pkg search -x 'linux-rl9-'.
- Run: /compat/linux/bin/PROGRAM or chroot /compat/linux /bin/bash.
- Look: sysctl compat.linux.osrelease, brandelf FILE, file FILE, truss PROGRAM.
- Missing library: chroot /compat/linux /usr/bin/ldd PROGRAM | grep 'not found', then pkg search -x 'linux-rl9.*NAME'.
- Network: copy /etc/resolv.conf into /compat/linux/etc/, and install linux-rl9-ca-certificates for HTTPS.
- Not available: systemd, cgroups, namespaces (so no Linux Docker or Podman; FreeBSD's own Podman runs simple Linux images, see the containers page).
12. The Boot Loader and Kernel Tuning: loader.conf, sysctl and Single-User Mode
The page: open it.
- Where settings live: /boot/loader.conf and /boot/loader.conf.d/*.conf (boot time), /etc/sysctl.conf (run time, read at multi-user), /etc/rc.conf (services). Never edit /boot/defaults/loader.conf.
- Look: sysctl NAME, sysctl -d NAME (description), sysctl -W NAME (writable now), sysctl -T NAME (boot tunable), kenv and kenv NAME (the loader's environment).
- Change live: sysctl NAME=VALUE. Make it stay: add NAME=VALUE to /etc/sysctl.conf, then service sysctl restart.
- Boot-only settings: NAME="VALUE" in a file in /boot/loader.conf.d/, then reboot.
- Which environment boots: zpool get bootfs zroot, bectl list, bectl activate NAME.
- At the menu: 1 multi-user, 2 single-user, 3 loader prompt, 4 reboot, 7 boot options, 8 boot environments, Space pauses.
- At the OK prompt: show, set NAME=VALUE, unset NAME, lsdev, boot, boot -s (single-user), boot -v (verbose).
- Single-user: root is read-only (zfs set readonly=off zroot/ROOT/default, zfs mount -a), fix, then exit.
- Serial console: console="comconsole,vidconsole" in a loader file.
13. Hardening FreeBSD: A Tested Baseline
The page: open it.
- Vulnerabilities: pkg audit -F (fetch and check), pkg audit -q, pkg -j NAME audit -F for a jail, pkg upgrade, freebsd-update fetch install. Read the advisory, then decide.
- SSH: sshd -T (effective settings), sshd -t (syntax check), service sshd reload. PasswordAuthentication no, KbdInteractiveAuthentication no, PermitRootLogin no, MaxAuthTries 3, AllowUsers NAME. First value wins. Test from a second connection.
- Information leaks: security.bsd.see_other_uids=0, see_other_gids=0, unprivileged_read_msgbuf=0, unprivileged_proc_debug=0. Also kern.randompid=1, kern.coredump=0. Persist in /etc/sysctl.conf, apply with service sysctl restart.
- Exposure: sockstat -4l -6l, service -e. syslogd_flags="-ss".
- Accounts: check shells and empty passwords in /etc/passwd and /etc/master.passwd; nologin for service accounts; wheel for su.
- Flags and levels: chflags schg|noschg FILE, ls -lo, sysctl kern.securelevel, kern_securelevel_enable="YES" and kern_securelevel="1" in rc.conf. A level can only be raised while running.
- Audit: auditd_enable="YES", /etc/security/audit_control (flags:lo,aa), audit -s (rotate), auditreduce FILES | praudit -s. Pair it with /var/log/auth.log.
14. Containers on FreeBSD: Podman, OCI Images and Jails
The page: open it.
- Install: pkg install podman-suite, a ZFS dataset on /var/db/containers, fdescfs on /dev/fd.
- Images: skopeo list-tags --override-os freebsd docker://docker.io/freebsd/freebsd-runtime, podman pull --os freebsd IMAGE:TAG, podman images, podman rmi.
- Run: podman run --rm --net host --os freebsd IMAGE COMMAND. In the background: -d --name NAME. Then podman ps, podman exec NAME COMMAND, podman stop NAME, podman rm NAME.
- See the jail: jls, ps -axo pid,jid,stat,command.
- Volumes: -v /host/path:/container/path.
- Build: a Containerfile (FROM, RUN, COPY, CMD), podman build --os freebsd --network host -t NAME:TAG DIR, podman history NAME:TAG.
- Linux images: podman pull --os linux --arch amd64 IMAGE, then podman run --os linux ... (needs linux_enable="YES").
- Networking: --net host always works; the default bridge needs pf.
- Inspect: podman info, podman system df.
15. FreeBSD in the Cloud: First-Boot Provisioning with nuageinit
The page: open it.
- Scripts: /etc/rc.d/nuageinit, nuageinit_post_net, nuageinit_user_data_script, growfs. Switch on with sysrc nuageinit_enable=YES growfs_enable=YES. Run once because of /firstboot.
- Config disk: an iso9660 or msdosfs disk labeled cidata (NoCloud) or config-2 (OpenStack), holding meta-data and user-data. Build with makefs -t cd9660 -o rockridge,label=cidata OUT.iso DIR.
- user-data: starts with #cloud-config (YAML) or with #! (a script). Keys: users, ssh_authorized_keys, ssh_pwauth, packages, write_files, runcmd, timezone, fqdn, network, chpasswd.
- Results: log in /var/log/nuageinit.log; host name in /etc/rc.conf.d/hostname.
- Disk growth: automatic with growfs (UFS and ZFS) on first boot.
- Extras: firstboot-freebsd-update, firstboot-pkg-upgrade, firstboot-pkgs, ec2-scripts, py312-cloud-init.
- Do not: leave the default user freebsd/freebsd, put secrets in user-data, or skip testing the first boot locally.
16. Monitoring FreeBSD: From top to Prometheus
The page: open it.
- Live: uptime, top -b, vmstat 1 3, swapinfo -h, iostat -x -c 2 -w 1 DISK, zpool iostat POOL 1 3, netstat -i -n, netstat -w 1 -q 3 -I IF. Interactive: gstat, systat.
- Quick health: zpool status -x, df -h, w, last.
- Nightly report: periodic daily|weekly|monthly. Defaults in /etc/defaults/periodic.conf, your changes in /etc/periodic.conf (daily_output, daily_status_zfs_enable, daily_scrub_zfs_enable). Default output goes to root's mail (mail, /var/mail/root).
- Logs: /var/log/messages keeps notice and above. logger -p daemon.notice -t TAG "text". Rotation by newsyslog (/etc/newsyslog.conf).
- Own checks: a script with logger, an exit status, sh -n, and a cron line (crontab -e, crontab -l, log in /var/log/cron).
- Prometheus: pkg install node_exporter prometheus, sysrc node_exporter_enable=YES node_exporter_listen_address="127.0.0.1:9100", sysrc prometheus_enable=YES prometheus_args="--web.listen-address=127.0.0.1:9090", promtool check config FILE, API: /api/v1/query?query=NAME, /api/v1/alerts, /api/v1/rules.
- Alert ideas: up == 0, disk space, zpool status -x, swap use, load, services, pkg audit.
- SNMP: sysrc bsnmpd_enable=YES, bsnmpget -s COMMUNITY@HOST OID. Change the default communities, and do not leave it open.
- Exposure: sockstat -4l -6l after installing any monitoring tool.
17. ZFS Backups to Another Machine: Send, Receive, Restore
The page: open it.
- Snapshot: zfs snapshot POOL/DATA@NAME, list with zfs list -t snapshot, what changed with zfs diff @OLD @NEW.
- Size first: zfs send -nv DATA@NEW (full) or zfs send -nv -i @OLD DATA@NEW (incremental).
- Full copy: zfs send DATA@NAME | ssh USER@HOST zfs receive -u POOL/BACKUPS/NAME.
- Incremental: zfs send -i @OLD DATA@NEW | ssh USER@HOST zfs receive -u POOL/BACKUPS/NAME. The old snapshot must exist on both sides.
- Protect the copy: zfs set readonly=on POOL/BACKUPS/NAME.
- Backup user: pw useradd zbackup -m, zfs allow -u zbackup create,mount,receive,send,destroy POOL/BACKUPS, add the user to sshd AllowUsers.
- One file back: zfs mount COPY, then read /MOUNT/.zfs/snapshot/NAME/path.
- Everything back: ssh USER@HOST zfs send COPY@NAME | zfs receive DATA, then compare checksums (find . -type f | sort | xargs sha256 -r).
- Interrupted: receive with -s, zfs get receive_resume_token COPY, zfs send -t TOKEN | ssh ... zfs receive -s -u COPY.
- "destination has been modified": zfs rollback COPY@LASTSNAP (or receive -F), and keep readonly=on.
- Script: newest snapshot on the backup side as the base, logger the result, prune with awk (FreeBSD head has no negative count), ssh -o ConnectTimeout=10, cron line 17 2 * * * /root/zbackup.sh.
18. WireGuard on FreeBSD: A Private Tunnel Between Two Machines
The page: open it.
- Is it there? ls /boot/kernel/if_wg.ko, wg --version. Both are in the base system. The module loads itself when the interface is created.
- Keys: (umask 077; wg genkey > NAME.key); wg pubkey < NAME.key > NAME.pub. Keep /etc/wireguard at mode 700, and never share the private key.
- Config (/etc/wireguard/wg0.conf, mode 600): [Interface] with PrivateKey and ListenPort, and one [Peer] per machine with PublicKey, AllowedIPs, optionally Endpoint and PersistentKeepalive = 25.
- Up by hand: ifconfig wg0 create, wg setconf wg0 FILE, ifconfig wg0 inet 10.10.10.1/24 up. Down: ifconfig wg0 destroy.
- Status: wg show, wg show wg0 latest-handshakes, wg show wg0 allowed-ips, wg show wg0 transfer.
- Change while running: wg set wg0 peer PUBKEY allowed-ips A,B. It replaces the list.
- AllowedIPs: outgoing routing and incoming filter in one. If the handshake works but the ping does not, check it on both sides.
- A network behind a peer: add it to allowed-ips, add a route (route add -net NET -interface wg0), and make the far side a router (net.inet.ip.forwarding=1).
- Persistent: sysrc cloned_interfaces+=wg0, sysrc ifconfig_wg0="inet 10.10.10.1/24", and /etc/start_if.wg0 with the wg setconf wg0 FILE line.
- Look at the wire: tcpdump -n -i vtnet1 udp port 51820 (noise) against tcpdump -n -i wg0 (the real traffic).
- Firewall: allow UDP 51820 in, then write rules for wg0 itself. ssh remembers host keys by address: a new tunnel address is a new host to ssh.
19. File Sharing with NFS: One Server, Many Machines
The page: open it.
- Server: sysrc nfs_server_enable=YES nfsv4_server_enable=YES nfsuserd_enable=YES mountd_enable=YES, then service nfsd start and service nfsuserd start.
- Exports (/etc/exports): V4: /zroot -network 192.168.99.0/24 and /zroot/share -network 192.168.99.0/24. Put the network on both lines for version 4. Reload with service mountd reload. List with showmount -e localhost.
- With ZFS: keep only the V4: line, then zfs set sharenfs="-network 192.168.99.0/24" zroot/share (file: /etc/zfs/exports).
- Client: mount -t nfs -o nfsv4 SERVER:/share /mnt/share (the path is relative to the V4: root). Options in use: nfsstat -m.
- Users: numbers, not names. Same uid on both sides. Root of the client is mapped to nobody. nfsuserd on both sides for names. Attribute cache up to 60 seconds.
- Persistent: sysrc nfs_client_enable=YES, fstab line SERVER:/share /mnt/share nfs rw,nfsv4,late 0 0.
- Server down: a hard mount waits (state D) and continues when the server returns. Avoid working directories inside the mount.
- Test the refusal: mount from a machine that should be denied.
- Exposure: sockstat -4l | grep -E ":(111|2049)", and firewall TCP 2049.
Where things live
- Services and boot-time switches: /etc/rc.conf, and drop-ins in /etc/rc.conf.d/ (read after it: nuageinit writes the host name there).
- Run-time kernel settings: /etc/sysctl.conf.
- Boot-time settings: /boot/loader.conf and /boot/loader.conf.d/*.conf. Defaults to read, never to edit: /boot/defaults/loader.conf.
- Firewalls: /etc/pf.conf (pf), a script such as /etc/ipfw.rules (ipfw).
- Jails: /etc/jail.conf, and the jail's own files under /jails/NAME.
- Packages: /usr/local for the software, /usr/local/etc/pkg/repos/ for extra repositories (make the folder first).
- Ports: /usr/ports, saved option choices in /var/db/ports, your build settings in /etc/make.conf.
- poudriere: /usr/local/etc/poudriere.conf, /usr/local/etc/poudriere.d/, and the finished packages under /usr/local/poudriere/data/packages/.
- Virtual machines: /zroot/vm (vm-bhyve), and the guests' disks as volumes under zroot/vm.
- Containers: /var/db/containers.
- Linux compatibility: /compat/linux.
- Cloud first boot: /firstboot, /var/log/nuageinit.log.
- SSH: /etc/ssh/sshd_config (first value wins).
- Nightly reports and your overrides: /etc/defaults/periodic.conf (read only), /etc/periodic.conf, reports in root's mail or in the file you name.
- Monitoring: /usr/local/etc/prometheus.yml and the rules file next to it, data in /var/db/prometheus.
- Logs and records: /var/log/messages (notice and above), /var/log/security (ipfw), /var/log/auth.log, /var/log/cron, and the audit trail in /var/audit.
- Passwords and accounts: /etc/passwd, /etc/master.passwd, group wheel.
The golden rules
These came up on page after page. They are why the pages say "test first" so often.
- Make a restore point first. bectl create NAME costs almost nothing and turns a bad upgrade, a bad boot setting or a bad hardening session into a reboot.
- Check the syntax before you load. pfctl -nf, sshd -t, sh -n on a script, promtool check config. A typo in a firewall, an SSH setting or a loader file is how people lock themselves out.
- Test from a second connection, and know where the console is. Do not close the first session until the second one works.
- Know the order. pf: the last matching rule wins (unless quick). ipfw: the lowest number wins, first match. In sshd_config the first value wins.
- Boot-time and run-time settings are different things. sysctl -W NAME and sysctl -T NAME tell you which one you have.
- Read the error message. FreeBSD's messages are usually specific: a read only tunable, Operation not permitted, The pf kernel module must be loaded. They name the next step.
- Do not mix firewalls on one machine, and remember that a default-deny firewall also blocks your virtual machines and containers until you let their bridge through.
- Patch both halves. freebsd-update for the base system, pkg upgrade for packages, and pkg audit -F to find out what is vulnerable. A build from an old ports tree is only as fresh as the tree.
- Prefer drop-in files. /boot/loader.conf.d/, /etc/rc.conf.d/, your own file in a repository folder. Your changes stay visible and easy to remove.
- Check what is listening after you install anything. sockstat -4l -6l. Exporters, agents and daemons often listen on every address by default.
- A check that cannot fail proves nothing. Break something on purpose (a degraded pool, a stopped exporter) and watch the alert.
- A snapshot is not a backup, and a backup is not proven until you restored it. Send to another machine, then destroy and restore on purpose, and compare checksums.
- Say what you did not test. The pages mark "from the documentation, not run here" on purpose. Do the same in your own notes.
What the corner does not cover
Honest gaps, so you know where to look elsewhere: off-site and cloud backup copies, encrypted raw sends and backup-management tools (sanoid, zrepl), dashboards and alert routing (Grafana and Alertmanager are named but not run), mail servers, bsdinstall interactive installs in detail, desktop use, and the other BSDs. The FreeBSD Handbook and the manual pages (man) go further than these pages in every direction.
You stop typing systemctl<br>
and start typing sysrc.
🧪 Try it yourself
Use this page as a quiz:
1. Pick five rows from "What do I want to do?" and, without looking, say the command. Then check. 2. Pick one of the four reading paths and follow it on a virtual machine. Keep a boot environment before each page. 3. Choose a golden rule you broke last month, and find the page that would have saved you.
← File Sharing with NFS: One Server, Many Machines · ↑ FreeBSD Corner