Who this is for
You have a Mac talking to a Samba server on a trusted LAN (NAS, homelab, Proxmox LXC). Transfer speed is much slower than your network capacity. You’ve read a dozen Stack Overflow threads and ended up with a config that mostly doesn’t help and occasionally makes things worse. This is the minimal set of changes that actually moves the needle.
I’ll assume you run a modern-ish Samba (4.15+) and macOS 13+. Everything below is SMB3 — SMB1 and SMB2 are not on the menu.
Step 1 — Server-side Samba tuning
Edit /etc/samba/smb.conf on the server. These go in the [global] section:
[global]
# SMB3 only. Skip the protocol-negotiation round-trip for older dialects.
server min protocol = SMB3
# Zero-copy from the filesystem to the socket. Huge win for large files.
use sendfile = yes
# Enable async I/O for requests ≥ 16 KB. Small files stay synchronous;
# big photos/videos drain through io_uring without blocking the worker.
aio read size = 16384
aio write size = 16384
aio max threads = 128
# Skip the receive-copy-into-userspace step for requests ≥ 16 KB.
# smbd does `recvfile(2)` straight to the destination file.
min receivefile size = 16384
# TCP_NODELAY disables Nagle. IPTOS_LOWDELAY signals the router.
# Do NOT set SO_SNDBUF / SO_RCVBUF here — Linux auto-tunes them,
# and explicit values disable the auto-tuning. I checked, testparm
# warns you about it, and it hurts more than it helps on modern kernels.
socket options = TCP_NODELAY IPTOS_LOWDELAY
# SMB3 multi-channel: no-op on single-NIC setups, but if you have
# two NICs bonded or a 2.5/10 GbE secondary, this lets macOS open
# parallel channels and scale.
server multi channel support = yes
Security settings in the same block (don’t weaken these unless you know what you’re doing):
server signing = mandatory
smb encrypt = required
map to guest = never
restrict anonymous = 2
disable netbios = yes
Signing + encryption cost some CPU. On an Intel N100 or similar the encryption overhead is negligible compared to the network. On a Raspberry Pi 4 it’s a real tax and you may want smb encrypt = desired (honor client request) instead of required.
Validate:
sudo testparm -s
Fix whatever warnings it prints, then:
sudo systemctl restart smbd
Step 2 — Client-side nsmb.conf on macOS
macOS reads /etc/nsmb.conf and ~/Library/Preferences/nsmb.conf on every mount. The user-scoped one doesn’t need root and is the one you want for a personal machine.
Create ~/Library/Preferences/nsmb.conf:
[default]
smb_neg=smb3_only
signing_required=no
streams_enabled=yes
Three lines. What they do:
smb_neg=smb3_only— refuse to negotiate SMB1 or SMB2. Forces the faster SMB3 dialect from the first packet. No fallback surprises.signing_required=no— let the server decide whether to sign. Matches the macOS default on 13+ anyway, just explicit here.streams_enabled=yes— expose macOS extended attributes over SMB (Finder metadata works).
Changes apply to new mounts. Unmount (eject in Finder) and remount to see them take effect:
umount /Volumes/your-share
# or just eject in Finder, then reconnect via ⌘K
What I deliberately didn’t tune
These settings show up in a lot of guides and don’t earn their keep:
- SO_SNDBUF / SO_RCVBUF in
socket options. Linux has been auto-tuning TCP buffers for fifteen years. Setting them explicitly disables the autotuning. On a LAN you usually get worse throughput, not better.testparmwarns you about this. read raw/write raw. These are SMB1 settings. Inert on SMB3. The Samba man page flags them as legacy.max xmit. Only affects SMB1 negotiation. No effect on SMB3.ea support = yes. Default on withvfs_fruit. Setting it again does nothing and confuses future-you.
Measure before/after
Claims without numbers are noise. Measure both ends:
# Baseline: raw network throughput between client and server.
iperf3 -c your.server.ip
# Expect ~940 Mbps on gigabit. If you see much less, your SMB tuning
# won’t save you — fix the network first.
# Actual SMB write throughput.
mount_smbfs //[email protected]/share /Volumes/test
dd if=/dev/zero of=/Volumes/test/bench.bin bs=1m count=1000 && \
rm /Volumes/test/bench.bin
On a gigabit LAN with the config above, I see reads around 110 MB/s and writes around 95–105 MB/s between my Mac and a 4-core LXC running on Proxmox. That’s basically the wire limit minus protocol overhead. Before tuning, the same setup was hovering around 12 MB/s with everything at defaults (mostly because aio read/write size = 1 meant nothing ever went async).
If it still feels slow
Check these in order:
- The network is the network.
iperf3first. If iperf is fast and SMB is slow, keep reading. If iperf is slow, the problem is a switch, a cable, a VLAN, or your ISP’s router in switch mode dropping jumbo frames. Fix that before touching SMB. - DNS. If your client resolves
nas.localvia mDNS and mDNS is flaky, every SMB operation pays the round-trip. Use a static IP or a proper DNS entry. Apple’sNetAuthAgentwill stall for tens of seconds on failed mDNS lookups. - Small file torture. SMB is bad at millions of tiny files. This is protocol-inherent, not a tuning fix. If you’re backing up a Photos library with 50k files, batch them, or stream them through a single long-lived connection instead of opening a new one per asset.
- Samba with
vfs_fruit.fruit:metadata = streamis the fast mode (stores Finder metadata in ADS). The defaultnetatalkmode rewrites the directory on every Finder interaction — it’s slow. If you’re not using this share from a Mac, dropfruitfromvfs objectsentirely.
The full smb.conf I run
For reference, my actual server config — a Samba share that exposes a backup directory to a single user over a trusted VLAN:
[global]
workgroup = WORKGROUP
server string = Homelab Backup
server role = standalone server
log file = /var/log/samba/log.%m
max log size = 1000
logging = file
server min protocol = SMB3
server signing = mandatory
smb encrypt = required
map to guest = never
restrict anonymous = 2
disable netbios = yes
use sendfile = yes
aio read size = 16384
aio write size = 16384
aio max threads = 128
min receivefile size = 16384
socket options = TCP_NODELAY IPTOS_LOWDELAY
server multi channel support = yes
interfaces = eth0
bind interfaces only = yes
[backup]
comment = Personal Backup
path = /srv/backup
valid users = backup
read only = no
browseable = yes
create mask = 0660
directory mask = 0770
force user = backup
force group = backup
vfs objects = fruit streams_xattr
fruit:metadata = stream
TL;DR
- Server side: enable sendfile, set
aio read/write size = 16384,min receivefile size = 16384,TCP_NODELAY IPTOS_LOWDELAYonly insocket options. - Client side: a three-line
~/Library/Preferences/nsmb.confforcing SMB3-only. - Don’t touch the SO_*BUF socket options on Linux. Auto-tuning is smarter than you.
- Measure with
iperf3andddbefore and after. If numbers don’t change, you’re tuning the wrong thing.
Everything above is what Byty, my photo-backup app for macOS, runs under the hood when you tap Optimize SMB client in the profile settings. If you’re the kind of person who reads blog posts like this, you probably also back up your Photos library to your own storage. Byty does that over SMB, with these settings wired in.