NVMe over Fabrics (NVMe-oF)
Protocol extension that allows NVMe commands to be transported over a network fabric. NVMe/TCP is the standard choice for most deployments — it runs over standard Ethernet without special hardware, offers lower latency than iSCSI due to the leaner NVMe command set, and is well-supported since Linux 5.0.
Other transports (RDMA/RoCE, Fibre Channel) exist but require specialized NICs/switches and are not covered here.
NVMe/TCP
Exposes a local block device as an NVMe namespace accessible to remote hosts over TCP/IP.
NQN Naming
NVMe Qualified Names follow the format:
nqn.YYYY-MM.reverse-domain:identifier
Example: nqn.2024-01.com.example:storage-pool-1
Every host also has its own NQN (used for access control):
cat /etc/nvme/hostnqn # view
nvme gen-hostnqn # generate if missingBasic Setup (Linux)
Target (server):
modprobe nvmet
modprobe nvmet-tcp
NQN="nqn.2024-01.com.example:storage-pool-1"
# Subsystem + namespace
mkdir /sys/kernel/config/nvmet/subsystems/$NQN
mkdir /sys/kernel/config/nvmet/subsystems/$NQN/namespaces/1
echo /dev/nvme0n1 > /sys/kernel/config/nvmet/subsystems/$NQN/namespaces/1/device_path
echo 1 > /sys/kernel/config/nvmet/subsystems/$NQN/namespaces/1/enable
# Port
mkdir /sys/kernel/config/nvmet/ports/1
echo 0.0.0.0 > /sys/kernel/config/nvmet/ports/1/addr_traddr
echo ipv4 > /sys/kernel/config/nvmet/ports/1/addr_adrfam
echo tcp > /sys/kernel/config/nvmet/ports/1/addr_trtype
echo 4420 > /sys/kernel/config/nvmet/ports/1/addr_trsvcid
ln -s /sys/kernel/config/nvmet/subsystems/$NQN \
/sys/kernel/config/nvmet/ports/1/subsystems/$NQNInitiator (client):
modprobe nvme-tcp
nvme discover -t tcp -a <server-ip> -s 4420
nvme connect -t tcp -a <server-ip> -s 4420 -n nqn.2024-01.com.example:storage-pool-1
nvme list # shows the connected device, e.g. /dev/nvme1n1Disconnect:
nvme disconnect -n nqn.2024-01.com.example:storage-pool-1
nvme disconnect-all # disconnects everythingProduction
Host Access Control
Never use attr_allow_any_host 1 in production. Instead, explicitly allow each initiator by its host NQN:
INITIATOR_NQN="nqn.2024-01.com.example:host-node1"
mkdir /sys/kernel/config/nvmet/subsystems/$NQN/allowed_hosts/$INITIATOR_NQNPersistence
The configfs tree is lost on reboot. Use nvmetcli (Python tool) to save and restore it:
nvmetcli save /etc/nvmet.json # snapshot current config
nvmetcli restore /etc/nvmet.json # restore it
nvmetcli clear # wipe all configSystemd service to restore on boot (/etc/systemd/system/nvmet.service):
[Unit]
Description=NVMe-oF Target
After=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=nvmetcli restore /etc/nvmet.json
ExecStop=nvmetcli clear
[Install]
WantedBy=multi-user.targetMultipath
NVMe native multipath merges multiple paths to the same subsystem into a single block device. Connect the same NQN over multiple IPs:
nvme connect -t tcp -a <ip1> -s 4420 -n <nqn>
nvme connect -t tcp -a <ip2> -s 4420 -n <nqn>
# kernel presents a single /dev/nvmeXnYEnable multipath in the kernel module config:
# /etc/modprobe.d/nvme.conf
options nvme_core multipath=1Check path status:
nvme list-subsysBacking Store
The namespace device_path accepts:
| Type | Example |
|---|---|
| Physical NVMe / block device | /dev/nvme0n1, /dev/sdb |
| LVM logical volume | /dev/vg0/lv_storage |
| Regular file (kernel 5.x+) | /mnt/pool/disk.img |
Network Recommendations
- Dedicated storage VLAN — isolates storage traffic and simplifies access control.
- Jumbo frames (MTU 9000) — reduces CPU overhead for large sequential I/O; must be set on both ends and all switches in the path.
- Bind target to a specific IP — use the actual storage interface IP in
addr_traddrinstead of0.0.0.0. - IRQ affinity — spread NVMe queue interrupts across cores for high-throughput workloads (
irqbalanceor manualsmp_affinity).
Security
By default NVMe/TCP has no encryption. Options:
- Network isolation — storage VLAN + firewall rules; simplest for trusted environments.
- TLS (kernel 6.3+) — native in-kernel TLS for NVMe/TCP 1.1; requires
nvme connect --tlsand pre-shared keys or certificates.