Andrew Mercer
on this page

FreeBSD SysAdmin Essentials: Packages, Ports, Filesystems, and Recovery

The everyday tasks that come up on any FreeBSD box: what version am I on, how do I install and patch software, how do I mount that USB stick or ISO, and how do I get back into a system that won't boot. It finishes with a map from old commands (the ones you'll still find in forum posts and old notes) to their modern replacements.

Related docs:

1. System Information

1.1 Version

freebsd-version -kru     # installed kernel, running kernel, installed userland
uname -rs                # running kernel name and release, e.g. "FreeBSD 15.0-RELEASE-p2"
uname -v                 # kernel build ident (GENERIC or your custom config) and build date

freebsd-version is the better first check. After freebsd-update patches userland but not the kernel (or the reverse), uname alone can show a different patch level from the one that's actually installed. A mismatch between -k and -r means you've installed a kernel but not rebooted into it yet.

1.2 Resources

sysctl hw.model hw.ncpu hw.physmem    # CPU model, core count, RAM in bytes
swapinfo -h                           # swap devices and usage, human-readable
top -SH                               # include system processes and threads
vmstat 1                              # memory, paging, CPU, one line per second
gstat -p                              # live per-disk I/O (physical providers only)
systat -vmstat 1                      # all-in-one live dashboard
df -h                                 # filesystem usage
zpool list && zfs list                # ZFS pool and dataset usage

On a ZFS system, "free" memory looks low because the ARC uses spare RAM as cache. Check top's ARC: line, or sysctl kstat.zfs.misc.arcstats.size, before deciding the box is short of memory.

1.3 Hardware and devices

dmesg | less                 # boot messages, device probing
tail -f /var/log/messages    # watch it while you plug something in
geom disk list               # every disk with size, model, serial
camcontrol devlist           # SATA/SAS/USB mass storage devices
gpart show                   # partition tables on all disks
pciconf -lv                  # PCI devices with vendor/device names
usbconfig                    # USB devices

2. Binary Packages with pkg

pkg is the package manager for prebuilt third-party software. Everything it installs lands under /usr/local (binaries, /usr/local/etc for config, /usr/local/etc/rc.d for services), separate from the base system.

2.1 Bootstrap

A fresh install doesn't include pkg itself, only a bootstrap stub. The first run downloads and installs it:

pkg bootstrap                      # or just run any pkg command and answer "y"
env ASSUME_ALWAYS_YES=yes pkg bootstrap -f    # non-interactive, for scripts

The repository signing keys ship with the base system under /usr/share/keys/pkg, so there's no need to fetch them by hand (old instructions that download a key file from svnweb are obsolete).

2.2 Repository configuration

The default repository is defined in /etc/pkg/FreeBSD.conf. Don't edit that file - it belongs to the base system and gets overwritten on upgrades. Override it from /usr/local/etc/pkg/repos/ instead:

mkdir -p /usr/local/etc/pkg/repos

Quarterly vs. latest. The default is the quarterly branch, which only receives security and build fixes during each quarter. latest tracks the ports tree head - newer software, more churn. To switch to latest:

# /usr/local/etc/pkg/repos/FreeBSD.conf  (FreeBSD 15.0 and later)
FreeBSD-ports: {
  url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest"
}
# /usr/local/etc/pkg/repos/FreeBSD.conf  (FreeBSD 14.x and earlier)
FreeBSD: {
  url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest"
}

FreeBSD 15.0 renamed the ports repository from FreeBSD to FreeBSD-ports (and FreeBSD-kmods to FreeBSD-ports-kmods). An override block has to use the name the base config uses, so overrides written for 14 silently stop applying after an upgrade to 15 - check with pkg -vv.

Disabling the default repo (when you run your own poudriere repository):

# /usr/local/etc/pkg/repos/FreeBSD.conf
FreeBSD-ports: { enabled: no }
# /usr/local/etc/pkg/repos/local.conf
local: {
  url: "https://pkg.example.internal/${ABI}-default",
  signature_type: "pubkey",
  pubkey: "/usr/local/etc/ssl/pkg/repo.pub",
  enabled: yes
}

Show the effective configuration and active repos:

pkg -vv | sed -n '/^Repositories:/,$p'

After changing repositories, refresh the catalogue and let pkg reconcile versions: pkg update -f && pkg upgrade.

2.3 Everyday commands

Task Command
Refresh the catalogue pkg update
Search pkg search nginx (pkg search -o nginx shows the port origin)
Install pkg install nginx
Upgrade everything pkg upgrade
Show installed packages pkg info (pkg info -x python matches by regex)
Details of one package pkg info nginx
Files a package installed pkg info -l nginx
Which package owns a file pkg which /usr/local/sbin/nginx
Post-install messages again pkg info -D nginx
Remove pkg delete nginx (pkg delete -g 'py311-*' for a glob)
Remove orphaned dependencies pkg autoremove
Hold a package at its version pkg lock nginx / pkg unlock nginx
Packages with available updates pkg version -vRL=
Verify installed file checksums pkg check -s -a
Clean the download cache pkg clean -a
Custom queries pkg query '%n-%v %sh' nginx (name, version, size)

Many ports split optional language modules into separate packages. Python is the classic example: sqlite3, tkinter and gdbm support come from py3XX-sqlite3, py3XX-tkinter and py3XX-gdbm, not from the main python3XX package. If an import fails on a module that's "part of the standard library," look for a matching py3XX-* package.

2.4 Vulnerability auditing

pkg audit checks installed packages against the FreeBSD VuXML vulnerability database:

pkg audit -F          # fetch the latest database, then audit
pkg audit -Fr         # also show which installed packages depend on a vulnerable one

The daily periodic(8) security run does this automatically and mails the results to root, so make sure root's mail goes somewhere you read (/etc/mail/aliases).

3. Building from Ports

The ports tree is the build recipe collection that the packages are produced from. Build from ports when you need a non-default compile option (make config), a patch, or software that isn't packaged.

3.1 Get the ports tree

portsnap was removed from the base system in FreeBSD 14.0, and the earlier cvsup/csup tooling long before that. The ports tree now comes from Git:

pkg install git      # or git-lite
git clone --depth 1 https://git.FreeBSD.org/ports.git /usr/ports              # head ("latest")
git clone --depth 1 -b 2026Q4 https://git.FreeBSD.org/ports.git /usr/ports    # a quarterly branch

Match the branch to your package repository (quarterly packages with a quarterly branch, latest with head) if you mix packages and ports, or dependency versions will drift apart. Quarterly branches are named YYYYQn.

Update later with:

git -C /usr/ports pull --ff-only

Read /usr/ports/UPDATING after each update - it lists manual steps for major version bumps (databases, Python default changes, and so on).

3.2 Build and install a port

cd /usr/ports/www/nginx
make config                 # choose options (saved under /var/db/ports)
make install clean          # build, install as a package, clean the work dir

Useful variations:

make config-recursive       # answer option dialogs for the port and all dependencies up front
make showconfig             # print the saved options
make rmconfig               # forget saved options
make -DBATCH install clean  # accept defaults, no dialogs (for scripts)
make deinstall              # uninstall the port's package
make deinstall reinstall    # rebuild with changed options
make pkg-descr && cat pkg-descr   # what the port is

Ports install through pkg, so pkg info, pkg which, pkg delete and pkg audit work the same way on port-built software.

Set site-wide defaults in /etc/make.conf, for example:

# /etc/make.conf
DEFAULT_VERSIONS+= python=3.11 pgsql=17 ssl=openssl
OPTIONS_UNSET+= DOCS EXAMPLES X11

3.3 Installing a port that's flagged vulnerable

The ports framework refuses to build a port with a known vulnerability. If you've judged the risk acceptable (for example, the vulnerable code path isn't used and no fixed version exists yet), override it per build:

cd /usr/ports/graphics/png
make DISABLE_VULNERABILITIES=yes install clean

Treat this as a temporary, documented exception, and re-check pkg audit -F until a fixed version lands.

3.4 Upgrading port-built software

Two classic ports-upgrade tools are still in the tree:

pkg install portmaster
portmaster -L --index-only    # list what has updates
portmaster -a                 # rebuild everything outdated

pkg install portupgrade
portversion -vL=              # list what has updates
portupgrade -a                # rebuild everything outdated

portmaster is a single shell script with no dependencies; portupgrade is Ruby-based and heavier, with a config file at /usr/local/etc/pkgtools.conf. Either is fine for a handful of machines.

For more than one or two hosts, or any production use, build packages in clean jails with poudriere and install them with pkg from your own repository (Section 2.2). That gives you repeatable builds and stops the "every host compiled something slightly different" problem. See the poudriere section of the FreeBSD and ZFS SysAdmin Lab.

Mixing packages and ports: it works, but upgrade them together. pkg upgrade will happily replace a port you built with custom options by the default package. Lock custom-built ports (pkg lock <name>) so pkg leaves them alone.

4. Mounting Filesystems

Find the device first: plug it in and watch tail -f /var/log/messages, or run gpart show and geom disk list. USB mass storage appears as da0, da1, ...; SATA/NVMe disks as ada0/nda0. Partitions are da0s1 on MBR-partitioned media and da0p1 on GPT. fstyp /dev/da0s1 tells you what filesystem a partition holds.

4.1 FAT (USB sticks, SD cards, EFI partitions)

mkdir -p /mnt/usb
mount -t msdosfs /dev/da0s1 /mnt/usb      # or: mount_msdosfs /dev/da0s1 /mnt/usb
umount /mnt/usb

Add -o longnames if names come through as 8.3, and -L en_US.UTF-8 (or your locale) to get non-ASCII filenames right. To let a non-root user mount removable media, set vfs.usermount=1 and give them write access to the device via devfs.rules.

exFAT isn't supported in the base system; use FUSE:

pkg install fusefs-exfat
kldload fusefs                     # sysrc kld_list+=fusefs to persist
mount.exfat /dev/da0s1 /mnt/usb

4.2 ISO images

ISO files are attached as memory disks with mdconfig, then mounted as cd9660:

mkdir -p /mnt/iso
mdconfig -a -t vnode -f /path/to/image.iso      # prints the device it created, e.g. md0
mount -t cd9660 /dev/md0 /mnt/iso
# ... use it ...
umount /mnt/iso
mdconfig -d -u 0                                # detach md0

One-liner version:

mount -t cd9660 /dev/$(mdconfig -f /path/to/image.iso) /mnt/iso

mdconfig -l -v lists attached memory disks and the files behind them - handy when you've lost track of which mdN is which. The same technique works for raw disk images (.img): attach, then gpart show md0 and mount the partition (/dev/md0p2 and so on).

4.3 Linux ext2/ext3/ext4

FreeBSD's ext2fs(5) driver reads and writes ext2, ext3 and ext4:

kldload ext2fs
mkdir -p /mnt/linux
mount -t ext2fs /dev/ada0p5 /mnt/linux
  • The driver doesn't replay the ext3/ext4 journal. Make sure the filesystem was cleanly unmounted by Linux (or fsck it from Linux first) before writing to it from FreeBSD.
  • Some newer ext4 features aren't supported for writing; if the mount falls back to read-only or refuses, mount with -o ro and do writes from Linux.
  • pkg install e2fsprogs gives you mkfs.ext4/mke2fs, e2fsck and tune2fs. Check a filesystem's feature set with tune2fs -l /dev/ada0p5.
  • Old notes warning that FreeBSD can only mount ext2 with 128-byte inodes (and telling you to mke2fs -I 128) date from FreeBSD 7. That limitation is long gone.

For sharing data between FreeBSD and Linux on the same machine, see Dual-Booting FreeBSD and Linux.

4.4 NTFS

pkg install fusefs-ntfs
kldload fusefs
ntfs-3g /dev/da0s1 /mnt/usb          # read-write
ntfs-3g -o ro /dev/da0s1 /mnt/usb    # read-only, if Windows left it hibernated ("Fast Startup")

4.5 Persistent mounts in /etc/fstab

# Device            Mountpoint   FStype    Options          Dump  Pass
/dev/gpt/data       /data        ufs       rw               2     2
/dev/da0s1          /mnt/usb     msdosfs   rw,noauto        0     0
/dev/gpt/linuxhome  /mnt/linux   ext2fs    rw,late          0     0

Use GPT labels (/dev/gpt/<label>, set with gpart modify -l <label> -i <index> <disk>) rather than raw device names so mounts survive disks being renumbered. noauto keeps removable media from blocking boot when it isn't plugged in; late mounts after modules like ext2fs are available (also add ext2fs to kld_list).

5. Formatting Removable Media

5.1 FAT32 USB stick

gpart destroy -F da0                  # wipe the existing partition table (destructive!)
gpart create -s mbr da0               # MBR is the most portable for USB sticks
gpart add -t fat32 da0                # one partition using the whole stick -> da0s1
newfs_msdos -F 32 -L MYLABEL /dev/da0s1

-F picks the FAT type (12, 16 or 32) and -L sets the volume label. Use GPT (gpart create -s gpt da0 and gpart add -t ms-basic-data da0) when the target machine expects it - for example, a FAT32 EFI system partition is gpart add -t efi -s 260M da0, then newfs_msdos -F 32 -c 1 /dev/da0p1.

5.2 Writing a bootable image

To write an installer or other disk image (FreeBSD memstick.img, a Linux ISO) to a USB stick:

dd if=FreeBSD-15.0-RELEASE-amd64-memstick.img of=/dev/da0 bs=1m conv=sync status=progress

Write to the whole device (da0), not a partition (da0s1). Double-check the device name with geom disk list first - dd will overwrite the wrong disk without asking.

6. Recovery: Getting Into a Broken System

6.1 Single-user mode

For a forgotten root password, a broken /etc/rc.conf, or a filesystem that needs fsck:

  1. At the boot menu, choose Single user (option 2), or escape to the loader prompt and type boot -s.
  2. Press Enter to accept /bin/sh as the shell.
  3. The root filesystem is mounted read-only. Make it writable and mount the rest:

UFS root:

sh fsck -p # check filesystems first mount -u / # remount / read-write mount -a -t ufs # mount the other UFS filesystems from /etc/fstab swapon -a # optional

ZFS root:

sh mount -u / # remount / read-write (or: zfs set readonly=off zroot/ROOT/default) zfs mount -a # mount the remaining datasets

  1. Do the repair (for example passwd root, or fix the broken line in /etc/rc.conf with vi; sysrc -x <var> deletes a variable).
  2. exit to continue to multi-user, or reboot.

If the console is marked insecure in /etc/ttys, single-user mode asks for the root password. Without it, boot from installation media instead (Section 6.3).

6.2 Roll back with a boot environment (ZFS)

If the system broke after an upgrade or config change and you have boot environments, the fastest fix is to boot the previous one: choose Boot Environments in the loader menu, pick the older BE, and boot. Once up, make it permanent with bectl activate <name>. bectl list shows them all, with N marking the running BE and R the one that will be used on reboot.

6.3 Rescue from installation media

When the system won't boot at all:

  1. Boot the FreeBSD installer from USB and choose Live System (or Shell from the installer menu).
  2. Mount the installed system under /mnt:

```sh # ZFS zpool import -f -R /mnt zroot zfs mount zroot/ROOT/default # the boot environment dataset, if not mounted automatically zfs mount -a

# UFS fsck -y /dev/ada0p2 mount /dev/ada0p2 /mnt ```

  1. Repair (chroot /mnt to run tools against the installed system), then unmount (zpool export zroot for ZFS) before rebooting, or the pool will need -f on the next import.

7. Legacy Commands and Their Modern Replacements

Old notes, forum posts and blog entries are full of tools that have since been removed. If you find one in a how-to, here's what to use instead:

Legacy Modern replacement Notes
sysinstall bsdinstall (install), bsdconfig (post-install menus), sysrc, pkg sysinstall was replaced in FreeBSD 9.0. To add the source tree after install, clone it with Git (see the kernel doc)
pkg_add -r, pkg_info, pkg_delete, pkg_version pkg install, pkg info, pkg delete, pkg version The old pkg_* tools were retired around FreeBSD 10 (2014)
setenv PACKAGESITE ... / PACKAGEROOT A repo override in /usr/local/etc/pkg/repos/ See Section 2.2
/var/db/pkg/<name>/ directories pkg info, pkg query /var/db/pkg now holds pkg's SQLite database, not per-package directories
portaudit -Fda pkg audit -F Built into pkg
cvsup / csup with a ports-supfile or stable-supfile git clone / git pull of ports.git or src.git CVS was retired years ago
portsnap fetch extract / portsnap fetch update git clone / git pull of ports.git portsnap was removed in FreeBSD 14.0
svn/svnlite checkouts of /usr/src git clone https://git.FreeBSD.org/src.git The Subversion repository was frozen after the move to Git
mergemaster -Ui etcupdate mergemaster was removed in FreeBSD 15.0
make -DBATCH with portaudit overrides make DISABLE_VULNERABILITIES=yes Section 3.3
fdisk, bsdlabel gpart fdisk is deprecated as of 15.0 and slated for removal
Boot floppies, fdformat USB memstick.img images, or PXE Floppy drives and the fdc(4) driver are on their way out
Vinum (gvinum) volumes ZFS or graid Removed from GEOM in FreeBSD 15.0
mount -u /mnt/cdrom to "unmount" umount /mnt/cdrom mount -u updates a mount's flags; it doesn't unmount
i386 builds amd64 (or arm64) 32-bit x86 was retired as a platform in FreeBSD 15.0

References