🚩 NetBSD vs Linux

What is the same, what is different and what is simply missing, on a real NetBSD 11.1 machine: services, packages, privileges, disks and network, with a Linux-to-NetBSD table to keep.

Video: NetBSD vs Linux (NetBSD video)
Prefer to watch? This short video runs this page's main commands on a real NetBSD 11.1 machine, with on-screen captions and a synthetic voiceover (an AI voice trained on the author's own recording). Click to play, or watch it on YouTube.
echo 'httpd=YES' >> /etc/rc.conf    # start at boot
/etc/rc.d/httpd start                 # start it now
pkgin -y install figlet               # install a package

If you know Linux you already know most of NetBSD: a Unix shell, the same everyday commands, files in the same places. This page covers the parts that are different, in the order you meet them: the manual, services without systemd, packages from pkgsrc, su instead of sudo, disks, the network and sysctl, with a Linux-to-NetBSD table at the end.

Every example ran for real on NetBSD 11.1 (amd64) in a virtual machine, as an ordinary user, with root commands marked #. The output you see is the output the machine gave.

What NetBSD is, and the lab

NetBSD is a complete Unix-like operating system, built to run on as many kinds of hardware as possible: PCs, servers, ARM boards, old workstations and more, all from one source tree. The kernel, the C library, the shell, the network tools and the manual pages are developed together, and a release has one version number for all of it. Software from outside the project comes from pkgsrc, a package collection that also works on Linux and macOS.

Every example below ran for real on NetBSD 11.1 (amd64) in a virtual machine, as an ordinary user called bsd. Where a command needs root it starts with # and is run as root, because NetBSD has no sudo by default. If you know Linux, most of this will feel familiar, with a few surprises.

$ uname -srm
NetBSD 11.1 amd64
$ echo $SHELL
/bin/sh
$ id
uid=1000(bsd) gid=100(users) groups=100(users),0(wheel)
$ sysctl kern.version | head -n 1
kern.version = NetBSD 11.1 (GENERIC) #0: Mon Nov  2 16:34:32 UTC 2026

The login shell is /bin/sh, the NetBSD Bourne shell, which is small and strict. It is a proper Almquist shell, not bash, so write scripts to POSIX rules. The user bsd is in the group wheel, which is what allows su to root.

The manual is part of the system

NetBSD documents itself well, and the pages are written with the code. Section 7 holds the overviews, which is where to start:

$ man -k hier | head -n 3
hier (7)	layout of file systems
hier
$ man hier 2>/dev/null | col -b | sed -n 1,10p
HIER(7) 	       Miscellaneous Information Manual 	       HIER(7)

NAME
     hier - layout of file systems

DESCRIPTION
     An outline of the file system hierarchy.

     Naming is very important.	The UNIX System relies on filename conventions
     for much of its power as a system.  The following file system layout
$ man -w afterboot
/usr/share/man/man8/afterboot.8
$ man -k pkgsrc | head -n 2
pkgsrc (7)	NetBSD packages collection (framework for third-party software)
...to perform various administrative tasks for the package system. pkg_add(1) , pkg_delete(1) , pkg_info(1) Alistair Crooks Hubert Feyrer The pkgsrc developers The pkgsrc guide, Documentation on the NetBSD packages system https://www.NetBSD.org/docs/pkgsrc/

hier(7) describes the file system layout, and afterboot(8) is the checklist for a new install. A manual page for each subsystem, and for each daemon, is the norm. man -k searches by keyword, the same as apropos.

The base system, and pkgsrc beside it

NetBSD keeps the system and the extra software strictly apart. The base system lives in /bin, /sbin, /usr/bin and /usr/sbin and is updated as a whole with the release. Everything you add from pkgsrc goes under /usr/pkg, with its own bin, etc and man directories.

$ ls /usr/pkg
bin
etc
include
lib
man
pkgdb
pkgdb.refcount
sbin
share
$ echo $PATH
/usr/bin:/bin:/sbin:/usr/sbin:/usr/pkg/bin:/usr/pkg/sbin:/usr/local/bin
$ ls /usr/pkg/etc | head -n 5
pkgin
$ ls /etc/rc.d | wc -l
     132

The rule is easy to remember: if it came with the release, it is in /usr or /; if you installed it, it is in /usr/pkg and its configuration is in /usr/pkg/etc. Linux distributions mix the two. Here, removing all of pkgsrc never touches the base system.

Services: rc.d and rc.conf

NetBSD has no systemd. Services are shell scripts in /etc/rc.d, and one file, /etc/rc.conf, decides which ones run at boot. A fresh install is small:

$ cat /etc/rc.conf | grep -v '^#' | grep .
if [ -r /etc/defaults/rc.conf ]; then
	. /etc/defaults/rc.conf
fi
rc_configured=YES
sshd=YES
hostname=nbsd
dhcpcd=YES
dhcpcd_flags="-qM -4 vioif0"
wscons=YES
$ grep -E '^(sshd|httpd|ntpd)=' /etc/defaults/rc.conf
ntpd=NO			ntpd_flags=""		# see below for ntpd_chrootdir
sshd=NO			sshd_flags=""
httpd=NO		httpd_flags=""
$ /etc/rc.d/sshd status
sshd is running as pid 834.
$ netstat -an -f inet | grep LISTEN
tcp        0      0  *.22                   *.*                    LISTEN

/etc/defaults/rc.conf lists every setting and its default, and you override only what you need in /etc/rc.conf. The listing above shows sshd running and listening on port 22, and nothing else. Now start a web server. bozohttpd is a small web server in the base system, and its rc.d script is called httpd. Give it a page, switch it on in rc.conf, then start it:

# echo "hello from NetBSD" > /var/www/index.html
# echo 'httpd=YES' >> /etc/rc.conf
# /etc/rc.d/httpd start
Starting httpd.
$ /etc/rc.d/httpd status
httpd is running as pid 1315.

The value in rc.conf makes it start at every boot, and the script starts it now. Check it from the machine itself:

$ ftp -o - http://127.0.0.1/ 2>&1
hello from NetBSD
$ netstat -an -f inet | grep LISTEN
tcp        0      0  *.80                   *.*                    LISTEN
tcp        0      0  *.22                   *.*                    LISTEN

ftp is also the NetBSD download tool, and it speaks http. Stop it again:

# /etc/rc.d/httpd stop
Stopping httpd.
# sed -i.bak '/^httpd=YES/d' /etc/rc.conf
$ /etc/rc.d/httpd status; echo "exit status $?"
$httpd is not enabled - see rc.conf(5).
Use the following if you wish to perform the operation:
  /etc/rc.d/httpd onestatus
exit status 1

With the line gone from rc.conf, the script refuses to run: the message is NetBSD's own and shows the name of the setting. onestatus ignores rc.conf. Every script takes start, stop, restart and status. Run it without arguments to see the list.

Packages: pkgin and pkg_add

The package tools come from pkgsrc. pkgin works like apt or dnf: it knows what exists, picks the dependencies and installs binary packages. Under it, pkg_add, pkg_info and pkg_delete do the work and are part of the system.

# pkgin -y install figlet
calculating dependencies...done.

1 package to install:
  figlet-2.2.5nb2

0 to remove, 0 to refresh, 0 to upgrade, 1 to install
92K to download, 661K of additional disk space will be used

[1/1] downloading figlet-2.2.5nb2.tgz done.
[1/1] installing figlet-2.2.5nb2...
pkg_install warnings: 0, errors: 0
reading local summary...
processing local summary...
$ pkg_info | head -n 4
pkg_install-20260227 Package management and administration tools for pkgsrc
sqlite3-3.53.4      SQL Database Engine in a C Library
pkgin-26.4.0        Apt / yum like tool for managing pkgsrc binary packages
tree-1.8.0          Print a text or HTML tree diagram of a directory structure
$ pkgin search figlet
figlet-2.2.5nb2 =    Print text banners in fancy ASCII art characters
figlet-fonts-20021023nb1  Additional fonts for figlet
toilet-0.3           Free replacement for the FIGlet utility

=: package is installed and up-to-date
<: package is installed but newer version is available
>: installed package has a greater version than available package
$ figlet NetBSD
 _   _      _   ____ ____  ____
| \ | | ___| |_| __ ) ___||  _ \
|  \| |/ _ \ __|  _ \___ \| | | |
| |\  |  __/ |_| |_) |__) | |_| |
|_| \_|\___|\__|____/____/|____/

$ pkg_info -FI /usr/pkg/bin/figlet
figlet-2.2.5nb2     Print text banners in fancy ASCII art characters

pkgin -y answers yes to its question. pkg_info lists what is installed, and -F with -I and a file name tells you which package owns it, much like dpkg -S. Packages install under /usr/pkg and never overwrite base files.

# pkgin -y remove figlet
1 packages to delete:
  figlet-2.2.5nb2

[1/1] removing figlet-2.2.5nb2...
pkg_install warnings: 0, errors: 0
reading local summary...
processing local summary...
$ pkg_info | grep -c figlet
0
(exit status 1)

The mirror comes from /usr/pkg/etc/pkgin/repositories.conf, which holds one line. Source packages are also available: pkgsrc is a tree of build recipes that you fetch with CVS or git and compile with make, which is how the binary packages are made.

su, wheel and no sudo

A new NetBSD install has no sudo and no doas. Administration is done as root, and you become root with su. Only members of the group wheel may do that, and the user created during the install is already in it:

$ id
uid=1000(bsd) gid=100(users) groups=100(users),0(wheel)
$ grep '^wheel' /etc/group
wheel:*:0:root,bsd
$ which sudo doas; echo "exit status $?"
exit status 3
$ man su 2>/dev/null | col -b | sed -n 20,30p
     is provided) is requested, and su switches to that user and group ID
     after obtaining a Kerberos ticket granting ticket.  A shell is then
     executed, and any additional shell arguments after the login name are
     passed to the shell.  su will resort to the local password file to find
     the password for login if there is a Kerberos error.  If su is executed
     by root, no password is requested and a shell with the appropriate user
     ID is executed; no additional Kerberos tickets are obtained.

     Alternatively, if the user enters the password "s/key", authentication
     will use the S/Key one-time password system as described in skey(1).
     S/Key is a Trademark of Bellcore.

su - starts a login shell as root, and prompts for the root password. On a machine with several administrators, install sudo or doas from pkgsrc with pkgin. Both are one line away, and they keep their configuration in /usr/pkg/etc.

Disks and file systems

NetBSD names disks by driver, like ld0 for a virtual disk, wd0 for SATA and sd0 for SCSI or USB. The disk is divided by a disklabel, and the root file system is the partition a:

$ df -h | head -n 3
Filesystem     Size   Used  Avail %Cap Mounted on
/dev/ld0a       17G   1.3G    15G   8% /
tmpfs          512M   4.0K   512M   1% /tmp
# disklabel ld0 | tail -n 6
4 partitions:
#        size    offset     fstype [fsize bsize cpg/sgs]
 a:  37748672        64     4.2BSD      0     0     0  # (Cyl.      0*-  37449*)
 b:   4194304  37748736       swap                     # (Cyl.  37449*-  41610*)
 c:  41942976        64     unused      0     0        # (Cyl.      0*-  41610*)
 d:  41943040         0     unused      0     0        # (Cyl.      0 -  41610*)
$ grep -v '^#' /etc/fstab | head -n 3
/dev/ld0a		/	ffs	rw		 1 1
/dev/ld0b		none	swap	sw,dp		 0 0
tmpfs		/tmp	tmpfs	rw,-m1777,-sram%25

The default install uses just two partitions: a for / and b for swap. The disk label says FFS for the file system, which is the BSD fast file system. Note that /etc/fstab names devices, not UUIDs. If you move a disk to another controller its name changes and the machine will not boot until you edit that file. Many people use wedge names (NAME=) or dk devices to avoid the problem.

Network configuration

There is no network manager. The installer started dhcpcd, the DHCP client, and the rc.conf line that follows tells it which interface to manage. A fixed address is a line in /etc/ifconfig.vioif0 instead.

$ grep dhcpcd /etc/rc.conf
dhcpcd=YES
dhcpcd_flags="-qM -4 vioif0"
$ ifconfig vioif0 | grep -E 'status|inet '
	status: active
	inet 10.0.2.15/24 broadcast 10.0.2.255 flags 0
$ route -n show -inet | head -n 5
Routing tables

Internet:
Destination        Gateway            Flags    Refs      Use    Mtu Interface
default            10.0.2.2           UG          -        -      -  vioif0
$ cat /etc/resolv.conf
# Generated by resolvconf
nameserver 10.0.2.3
$ cat /etc/ifconfig.vioif0
up

The name of the interface shows the driver: vioif is the virtio network card here, and a PC would show something like re0 or wm0. /etc/resolv.conf is written by resolvconf, so do not edit it by hand.

sysctl, securelevel and kernel modules

The kernel settings are read and written with sysctl, and /etc/sysctl.conf is applied at boot:

$ sysctl hw.ncpu hw.physmem
hw.ncpu = 2
hw.physmem = 2146942976
$ sysctl kern.securelevel
kern.securelevel = -1
$ grep -v '^#' /etc/sysctl.conf | grep .
ddb.onpanic?=0
net.inet6.ip6.accept_rtadv=0
$ modstat | head -n 4
NAME                       CLASS    SOURCE   FLAG  REFS    SIZE REQUIRES
aac                        driver   builtin  -        1       - pci
accf_dataready             misc     builtin  -        0       - -
accf_httpready             misc     builtin  -        0       - -

kern.securelevel is -1, the permanently insecure mode, which is the default. On OpenBSD the same value is 1 after boot. NetBSD lets you raise the level in /etc/rc.conf with the setting securelevel, but does not do it for you. modstat lists the kernel modules, and modload and modunload add and remove them while the system runs.

Linux to NetBSD side by side

Most commands you know have a close cousin here:

 - systemctl enable and start: put name=YES in /etc/rc.conf, then /etc/rc.d/name start.
 - journalctl: the logs are plain text files in /var/log, written by syslogd.
 - apt or dnf: pkgin for binary packages, pkgsrc for building from source.
 - dpkg -S: pkg_info -F file.
 - sudo: su for root, or install sudo or doas from pkgsrc.
 - NetworkManager: dhcpcd plus /etc/ifconfig.<interface>.
 - /etc/fstab with UUIDs: /etc/fstab with device names.
 - lsblk: disklabel and dmesg.
 - ip addr: ifconfig. ip route: route.
 - /proc: it exists, and so does /kern.
 - bash: sh, and bash from pkgsrc if you want it.

Config files are in /etc for the system and /usr/pkg/etc for packages. When in doubt, man hier explains where a file belongs.

When something goes wrong

The first errors you will meet are about permissions and about a search that finds nothing:

$ pkgin -y install figlet
pkgin: You don't have enough rights for this operation.
(exit status 1)
$ pkgin search nosuchpackage-xyz; echo "exit status $?"
No results found for nosuchpackage-xyz
exit status 1
$ nosuchcommand
sh: nosuchcommand: not found
(exit status 127)
$ /etc/rc.d/nosuchservice start
sh: /etc/rc.d/nosuchservice: not found
(exit status 127)

pkgin refuses to install without root, and says so plainly. A search that finds nothing prints a message and a non-zero exit status, which scripts can test. A command that is not installed is not found, and a service script that does not exist is simply an error about the file. When a daemon fails to start, the reason is usually in /var/log/messages, and dmesg shows the kernel messages.