← Back to Wiki
Networking / Storage

Your "10 Gigabit" NFS Mount May Be Running Over the 1 Gigabit Link

A second, faster path was added between a Proxmox VE guest and a Synology NAS. The mount was pointed at the new 10GbE address. Routing confirmed the new interface. mount showed exactly what it should. Throughput didn't move a single megabyte per second — because NFSv4 recognised the server it was already talking to and quietly kept using the old connection.

Share on X

What server trunking is

NFSv4.1 and later can use multiple network paths to the same server. To do that, the client has to work out whether two addresses are in fact the same server — which it does with an EXCHANGE_ID call. If the server answers with an identity the client already has a session with, the client treats the new address as another path to a known server and reuses the existing transport instead of establishing a new one.

That's a sensible feature. It is also exactly why your new fast path can carry nothing: you mounted a different IP, the client asked who lives there, the answer was "the server you're already connected to", and it carried on using the connection it had — over the old, slow interface.

Why every obvious check passes

This is the part that makes it eat an afternoon. All of these look correct while the problem is happening:

$ mount | grep movies
10.0.1.31:/volume1/movies on /mnt/movies type nfs4 (rw,...)   # the new address ✓

$ ip route get 10.0.1.31
10.0.1.31 dev ens20 src 10.0.1.44                              # the new interface ✓

$ ping -c3 10.0.1.31
3 packets transmitted, 3 received                              # reachable ✓

Throughput was unchanged at 105 MB/s — suspiciously close to what a saturated 1 GbE link gives you, which was the only clue that anything was wrong.

The check that actually answers it: interface counters

Ask the interface how many bytes it has moved. It cannot be talked into a wrong answer:

# before
$ cat /sys/class/net/ens20/statistics/rx_bytes
0

# read ~4 GB off the mount
$ dd if=/mnt/movies/some.large.file of=/dev/null bs=1M count=4000

# after
$ cat /sys/class/net/ens20/statistics/rx_bytes
0                       # <- the "10G" interface received nothing at all

Zero. The traffic was going out of the old interface the whole time. Throughput numbers and mount output are not evidence about which link is carrying traffic; counters are. After the fix, the same test showed roughly 4,028 MB across that interface for a 4 GB read — which is what "it's actually using this path" looks like.

nfsstat -m and ss -tnp | grep :2049 are useful supporting checks: the latter shows you the local address of the actual TCP connection, which is the thing in dispute.

NFS client mounts 10G addr vers=4 NAS both addresses same server id existing 1G connection — all traffic stays here new 10G path — rx_bytes: 0 EXCHANGE_ID → "you already have a session with me" → client reuses the old transport
Trunking detection in one picture. The mount is correct, the route is correct, and the packets never touch the new link.

The fix: pin it to NFSv3, deliberately

NFSv3 has no trunking detection. It has no concept of server identity across addresses, so the address you ask for is the address it uses. For a fixed storage path where you have decided which link the traffic should take, that predictability is the feature:

# /etc/fstab
10.0.1.31:/volume1/movies  /mnt/movies  nfs  vers=3,nconnect=8,_netdev  0  0

Write that choice down where the next person will find it, because vers=3 looks like somebody was being lazy or hadn't upgraded. It isn't an oversight — it's the mechanism. If you later move back to v4 for its other benefits, re-verify with interface counters, not with mount output or a throughput number.

If you want to stay on v4, then the honest options are to make the two addresses genuinely distinct servers from the client's point of view, or to accept that path selection is the client's decision rather than yours. There is no "please use this NIC" flag that beats trunking.

While you're here: one connection isn't enough from a VM

Once traffic was genuinely on the fast link, a single NFS connection from the guest still only reached 299 MB/s, while the hypervisor itself managed 579–633 MB/s over the same link. That gap is virtio single-queue overhead, not the network — one queue, one CPU doing the work.

nconnect tells the client to open several TCP connections for the same mount, which spreads that work:

ConfigurationThroughput
1G path (before)105 MB/s
10G, single connection299 MB/s
10G, nconnect=8553 MB/s
10G, nconnect=8, live library under load397 MB/s

Nearly double, from one mount option. nconnect needs a reasonably modern kernel on both ends and is per server-address, so mounts to the same address share the connections. Raising the guest NIC's queue count (queues=8 on a virtio NIC) attacks the same overhead from the other side and stacks with it, at the cost of a guest restart.

Prevention

The one-line version: NFSv4 will recognise a server it already has a session with and keep using the old connection no matter which address you mounted, so a new fast link can sit at zero bytes while everything looks correct — check /sys/class/net/<iface>/statistics/rx_bytes, pin storage mounts to vers=3 when you need to choose the path yourself, and add nconnect before concluding the link is the limit.