How to Set Up FreeBSD as a NAS with ZFS and Samba
How to design a resilient FreeBSD ZFS NAS, configure a supported Samba package, align identities and permissions, and verify backup and client access.
FreeBSD, OpenZFS, and Samba form a capable NAS stack, but the file server is only one part of the result. Pool geometry, device identity, dataset policy, Unix ownership, Samba identities, client behavior, monitoring, and independent backup all have to agree. A share appearing in Finder or Explorer proves discovery, not durability or correct authorization.
Inventory disks before any destructive command
Capture hardware and existing storage state from console:
freebsd-version -kru
camcontrol devlist
gpart show -p
zpool status -v
zfs list -o name,used,avail,mountpoint
zpool create is destructive. Never paste device names from an example or assume that da0 still identifies the same disk after cabling or controller changes. Use persistent GPT labels or another verified stable naming scheme, record serial numbers and bays, and confirm every selected device contains no needed data.
Pool geometry remains a long-lived decision even though current OpenZFS has gained more expansion capabilities. A four-disk mirror-of-mirrors offers strong random I/O and can lose one disk from each mirror, but loses the pool if both members of one mirror fail. Four-disk RAIDZ2 can lose any two disks and provides roughly half the raw capacity before overhead. Neither layout is “RAID 10 versus RAID 6” in every operational detail; model usable space, workload, rebuild time, and future expansion with the actual disk sizes.
For four blank, correctly labeled devices, a RAIDZ2 example is:
zpool create -o ashift=12 tank raidz2 \
/dev/gpt/nas0 /dev/gpt/nas1 /dev/gpt/nas2 /dev/gpt/nas3
zpool status -v tank
This is an example, not a recommendation for every workload. ashift and vdev design need hardware-specific review. Do not create the pool until a tested backup exists for anything being migrated.
Create datasets around policy boundaries
Separate datasets allow different quotas, snapshots, replication, and permissions without making multiple pools:
zfs create -o compression=lz4 tank/media
zfs create -o compression=lz4 tank/backups
zfs create -o compression=zstd tank/documents
zfs set quota=2T tank/backups
zfs list -o name,used,avail,compressratio,mountpoint
Compression is transparent, but expected savings depend on data. Video, encrypted archives, and many backups are already compressed; documents or source trees may benefit more from Zstandard. Measure CPU and compression ratio before applying an aggressive level broadly. Deduplication is not a generic NAS optimization and can demand substantial memory; do not enable it without a workload-specific capacity model.
Quotas protect shared pool space but can make a client fail abruptly when reached. Alert before the limit. Decide snapshot and replication schedules per dataset, and keep application data that needs consistent backup separate from disposable caches.
ZFS checksums detect corruption and redundant vdevs can repair it when a good copy exists. That does not replace backups: deletion, ransomware, administrator error, pool loss, theft, and fire can affect every local copy and snapshot.
Install a supported Samba branch
The old samba419 package is now marked deprecated in FreeBSD Ports. Query the configured repository and select a supported branch available there:
pkg update
pkg search -o '^samba4[0-9]+-'
pkg audit -F
At the time of this review, FreeBSD Ports contains supported Samba 4.22 and 4.23 branches; for a repository offering 4.23, installation is:
pkg install samba423
Do not force that suffix when the deployed repository offers a different supported branch. Read the package message, record build options, and plan security updates. Samba’s server configuration is /usr/local/etc/smb4.conf.
Align Unix groups and dataset permissions
Create accounts without interactive shells when they exist only for file ownership and SMB authentication:
pw groupadd mediausers
pw useradd alice -m -s /usr/sbin/nologin
pw groupmod mediausers -m alice
chown root:mediausers /tank/media
chmod 2770 /tank/media
The setgid bit makes new entries inherit the directory’s group. Avoid chmod -R on a populated share: it can erase intentional restrictions and does not repair complex ACL semantics safely. Inspect a representative tree first with find, stat, and getfacl, then design a migration if existing ownership is inconsistent.
Samba authorization cannot grant access that the underlying filesystem denies. Conversely, permissive Unix modes can expose data through another local service even when Samba’s valid users is narrow. Treat both layers as required controls.
Build and validate a minimal Samba configuration
A standalone server using Samba’s recommended local tdbsam database can begin with:
[global]
workgroup = WORKGROUP
server role = standalone server
security = user
passdb backend = tdbsam
map to guest = Never
log file = /var/log/samba4/log.%m
max log size = 1000
[media]
path = /tank/media
valid users = @mediausers
read only = no
create mask = 0660
directory mask = 0770
Do not add SMB1 compatibility unless a documented legacy requirement and compensating controls justify it. Modern clients should use current SMB dialects. Avoid guest access for private data, and do not advertise a share that points into the pool root.
Validate syntax and the effective configuration before enabling anything:
testparm -s /usr/local/etc/smb4.conf
The current FreeBSD Handbook uses pdbedit to map an existing Unix account into Samba’s account database. The former smbpasswd default is obsolete:
pdbedit -a -u alice
pdbedit -L
Use a unique password delivered through an approved channel. A nologin shell prevents normal shell login but does not by itself disable every other service; audit SSH and other authentication paths separately.
Start the service and restrict network exposure
Enable Samba through FreeBSD’s rc system only after testparm passes:
sysrc samba_server_enable=YES
service samba_server start
service samba_server status
sockstat -4 -6 -l | grep -E 'smbd|nmbd|winbindd'
Allow SMB only from trusted client networks. Direct-hosted SMB normally uses TCP 445; NetBIOS discovery and older clients can involve ports 137–139. Do not expose any of them to the public Internet. Validate the complete firewall before reloading it and preserve remote administrative access.
Query locally before blaming the network:
smbclient -L localhost -U alice
tail -n 100 /var/log/samba4/log.smbd
Log filenames vary with configuration and client expansion, so inspect the configured directory. Monitor authentication failures without retaining sensitive logs indefinitely.
Test permissions, durability, and recovery from real clients
From Windows, connect to \\nas-ip\media; from macOS use smb://nas-ip/media. Test with the exact user and groups intended for production. Create a directory and file, rename them, replace content, delete them, and confirm another authorized user receives the expected access. Also test an unauthorized account and guest connection: a negative test catches accidental broad access.
Check ownership and modes on FreeBSD after client writes. Test filenames with spaces and Unicode, large files, reconnect after sleep, concurrent modification, quota warning behavior, and recovery after a controlled Samba restart. If macOS metadata or Windows ACL behavior matters, design and test the appropriate Samba VFS modules rather than adding copied options without understanding their storage effects.
Schedule ZFS scrubs and monitor completion, capacity, SMART or controller health, checksum errors, degraded vdevs, snapshot growth, and Samba failures:
zpool scrub tank
zpool status -v tank
zfs list -t snapshot
Finally, restore representative files from independent backup to another location and compare contents and permissions. A backup job reporting success is not a proven restore. The NAS is ready only when access controls, storage health, alerts, off-host recovery, and client workflows have all been exercised.
Related:
- Recovering a ZFS Pool That Won’t Import on FreeBSD
- ZFS on FreeBSD: Pools, Datasets, and Snapshots Explained
Sources: