← Back to Blog
April 19, 2026 Homelab

Optimize SMB Throughput on macOS and Samba

My homelab SMB share was pushing 12 MB/s. The wire is gigabit. I was leaving 900 Mbps on the floor for no good reason. Here are the exact server-side Samba knobs and client-side nsmb.conf lines that got me to the actual wire limit — plus the ones that look tempting but just warn you in testparm.

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. testparm warns 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 with vfs_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:

  1. The network is the network. iperf3 first. 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.
  2. DNS. If your client resolves nas.local via mDNS and mDNS is flaky, every SMB operation pays the round-trip. Use a static IP or a proper DNS entry. Apple’s NetAuthAgent will stall for tens of seconds on failed mDNS lookups.
  3. 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.
  4. Samba with vfs_fruit. fruit:metadata = stream is the fast mode (stores Finder metadata in ADS). The default netatalk mode rewrites the directory on every Finder interaction — it’s slow. If you’re not using this share from a Mac, drop fruit from vfs objects entirely.

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_LOWDELAY only in socket options.
  • Client side: a three-line ~/Library/Preferences/nsmb.conf forcing SMB3-only.
  • Don’t touch the SO_*BUF socket options on Linux. Auto-tuning is smarter than you.
  • Measure with iperf3 and dd before 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.